Add stage layer asset sources
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
# Stage 01 Assets
|
||||||
|
|
||||||
|
This directory is reserved for the first playable side-scrolling ACT stage.
|
||||||
|
|
||||||
|
## Slots
|
||||||
|
|
||||||
|
| Slot | Expected file | Notes | Source / License |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Far background | `background/background_far.png` | Large or horizontally tileable image. | TBD |
|
||||||
|
| Mid background | `background/background_mid.png` | Parallax midground image or prop sheet. | TBD |
|
||||||
|
| Tileset | `tiles/stage_01_tileset.png` | Ground, platform, wall, edge tiles. | TBD |
|
||||||
|
| Props sheet | `props/stage_01_props.png` | Crates, signs, lamps, blockers, exits. | TBD |
|
||||||
|
| Hit spark | `effects/hit_spark.png` | Small combat impact effect. | TBD |
|
||||||
|
| Dust | `effects/dust.png` | Run, jump, and landing dust. | TBD |
|
||||||
|
| Music / ambience | `audio/` | Optional loopable stage audio. | TBD |
|
||||||
|
|
||||||
|
## Import Rules
|
||||||
|
|
||||||
|
- Keep file names lowercase with underscores.
|
||||||
|
- Prefer PNG for visual assets.
|
||||||
|
- Record the original download URL, author, license, and any attribution text before using an asset.
|
||||||
|
- Keep collision data separate from visual tiles; visual alignment should stay within 4 pixels of collision.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -201,11 +201,14 @@ mindmap
|
|||||||
- `movement/platform_world.*`
|
- `movement/platform_world.*`
|
||||||
- `stage/stage_renderer.*`
|
- `stage/stage_renderer.*`
|
||||||
- `StageLayer` 灰盒数据已接入:远景、中景、地面/平台、后景 props 先用色块占位。
|
- `StageLayer` 灰盒数据已接入:远景、中景、地面/平台、后景 props 先用色块占位。
|
||||||
|
- `StageLayerSource` 已接入:每个 `StageRect` 可选择色块占位或 sprite 资源路径,加载失败时回退色块。
|
||||||
|
- `StageAssetSlots` 已建立:`stage_01` 资源目录和素材来源记录已预留,素材回来后可直接落位。
|
||||||
- 第一关宽度已扩展到 3200 像素,并加入 3 个 battle zone 占位与敌人出生点。
|
- 第一关宽度已扩展到 3200 像素,并加入 3 个 battle zone 占位与敌人出生点。
|
||||||
|
|
||||||
下一步:
|
下一步:
|
||||||
|
|
||||||
- 将 `StageLayer` 色块替换为 tileset、背景图和 props 素材。
|
- 将 `StageLayer` 色块逐步替换为 tileset、背景图和 props 素材。
|
||||||
|
- 将后景/前景 props 统一到 `StageLayer` 数据。
|
||||||
- 将 battle zone 占位升级为触发、锁镜头、清场解锁逻辑。
|
- 将 battle zone 占位升级为触发、锁镜头、清场解锁逻辑。
|
||||||
- 将敌人出生点接入 `EnemyActor`。
|
- 将敌人出生点接入 `EnemyActor`。
|
||||||
|
|
||||||
@@ -580,8 +583,8 @@ mindmap
|
|||||||
|
|
||||||
下一步建议按这个顺序做:
|
下一步建议按这个顺序做:
|
||||||
|
|
||||||
1. `StageLayerSource`:让 `StageLayer` 同时支持色块占位和 sprite 资源路径。
|
1. 已完成:`StageLayerSource` 让 `StageLayer` 同时支持色块占位和 sprite 资源路径。
|
||||||
2. `StageAssetSlots`:建立 `stage_01` 资源槽位和素材来源记录,做到素材可直接落位。
|
2. 已完成:`StageAssetSlots` 建立 `stage_01` 资源槽位和素材来源记录,做到素材可直接落位。
|
||||||
3. `StageProps`:把后景/前景 props 统一到 layer 数据。
|
3. `StageProps`:把后景/前景 props 统一到 layer 数据。
|
||||||
4. `BattleZone`:把 `F2` debug 清场替换为敌人清空自动解锁。
|
4. `BattleZone`:把 `F2` debug 清场替换为敌人清空自动解锁。
|
||||||
5. `EnemyActor`:做第一个敌人占位,接入出生点。
|
5. `EnemyActor`:做第一个敌人占位,接入出生点。
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
- `CameraZoneLock` 已接入:玩家进入 battle zone 后摄像机临时锁定到 zone camera bounds,`F2` 临时清场并恢复全关卡跟随。
|
- `CameraZoneLock` 已接入:玩家进入 battle zone 后摄像机临时锁定到 zone camera bounds,`F2` 临时清场并恢复全关卡跟随。
|
||||||
- 已修复 zone camera bounds 小于 1280 视口导致摄像机锁死的问题;所有锁镜头范围必须不小于当前视口,debug 阶段玩家离开 zone 会自动清场解锁。
|
- 已修复 zone camera bounds 小于 1280 视口导致摄像机锁死的问题;所有锁镜头范围必须不小于当前视口,debug 阶段玩家离开 zone 会自动清场解锁。
|
||||||
- 已修复进入 battle zone 时摄像机硬切的问题;zone camera bounds 表示摄像机可移动范围,进入/离开 zone 都通过短过渡平滑切换约束。
|
- 已修复进入 battle zone 时摄像机硬切的问题;zone camera bounds 表示摄像机可移动范围,进入/离开 zone 都通过短过渡平滑切换约束。
|
||||||
|
- `StageLayerSource` 已接入:`StageRect` 支持色块占位或 sprite 资源路径,资源不存在时回退色块。
|
||||||
|
- `StageAssetSlots` 已建立:`game/assets/stage/stage_01/` 已预留 background、tiles、props、effects、audio 目录和素材来源记录。
|
||||||
|
|
||||||
## 2. 第一关场景目标
|
## 2. 第一关场景目标
|
||||||
|
|
||||||
@@ -254,8 +256,8 @@ game/assets/stage/stage_01/
|
|||||||
|
|
||||||
1. 已完成:`CameraController` 修复当前摄像机不会动的问题,实现玩家跟随、边界 clamp、dead zone 和平滑参数。
|
1. 已完成:`CameraController` 修复当前摄像机不会动的问题,实现玩家跟随、边界 clamp、dead zone 和平滑参数。
|
||||||
2. 已完成:`CameraZoneLock` 让 battle zone 可以锁定摄像机范围,先用 `F2` 做 debug 清场和解锁占位。
|
2. 已完成:`CameraZoneLock` 让 battle zone 可以锁定摄像机范围,先用 `F2` 做 debug 清场和解锁占位。
|
||||||
3. `StageLayerSource`:扩展 `StageLayer`,让每个 rect 支持色块占位或 sprite 资源路径。
|
3. 已完成:`StageLayerSource` 扩展 `StageLayer`,让每个 rect 支持色块占位或 sprite 资源路径。
|
||||||
4. `StageAssetSlots`:创建并记录 `stage_01` 资源槽位,不要求已有最终素材。
|
4. 已完成:`StageAssetSlots` 创建并记录 `stage_01` 资源槽位,不要求已有最终素材。
|
||||||
5. `StageProps`:把 props 从旧 `WhiteboxProp` 迁移到统一 layer 数据,支持 back/front 两层。
|
5. `StageProps`:把 props 从旧 `WhiteboxProp` 迁移到统一 layer 数据,支持 back/front 两层。
|
||||||
6. `SceneDebug`:补齐 F1 debug 总开关,并预留分项开关。
|
6. `SceneDebug`:补齐 F1 debug 总开关,并预留分项开关。
|
||||||
7. `SceneAcceptanceRun`:PC 和 Switch 都构建通过,PC 能从起点移动到终点并看到相机推进。
|
7. `SceneAcceptanceRun`:PC 和 Switch 都构建通过,PC 能从起点移动到终点并看到相机推进。
|
||||||
|
|||||||
@@ -16,10 +16,20 @@ enum class StageLayerRole {
|
|||||||
PropsFront
|
PropsFront
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StageLayerSource {
|
||||||
|
std::string texturePath;
|
||||||
|
frostbite2D::Rect sourceRect;
|
||||||
|
bool useSourceRect = false;
|
||||||
|
frostbite2D::Color tint = frostbite2D::Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
bool hasTexture() const { return !texturePath.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
struct StageRect {
|
struct StageRect {
|
||||||
frostbite2D::Rect bounds;
|
frostbite2D::Rect bounds;
|
||||||
frostbite2D::Color color;
|
frostbite2D::Color color;
|
||||||
StageLayerRole layer = StageLayerRole::PropsBack;
|
StageLayerRole layer = StageLayerRole::PropsBack;
|
||||||
|
StageLayerSource source;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SpawnPoint {
|
struct SpawnPoint {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ns_game::stage_assets::stage_01 {
|
||||||
|
|
||||||
|
constexpr const char* kBackgroundFar =
|
||||||
|
"assets/stage/stage_01/background/background_far.png";
|
||||||
|
constexpr const char* kBackgroundMid =
|
||||||
|
"assets/stage/stage_01/background/background_mid.png";
|
||||||
|
constexpr const char* kTileset =
|
||||||
|
"assets/stage/stage_01/tiles/stage_01_tileset.png";
|
||||||
|
constexpr const char* kProps =
|
||||||
|
"assets/stage/stage_01/props/stage_01_props.png";
|
||||||
|
constexpr const char* kHitSpark =
|
||||||
|
"assets/stage/stage_01/effects/hit_spark.png";
|
||||||
|
constexpr const char* kDust =
|
||||||
|
"assets/stage/stage_01/effects/dust.png";
|
||||||
|
|
||||||
|
} // namespace ns_game::stage_assets::stage_01
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include <frostbite2D/graphics/camera.h>
|
#include <frostbite2D/graphics/camera.h>
|
||||||
#include <frostbite2D/graphics/renderer.h>
|
#include <frostbite2D/graphics/renderer.h>
|
||||||
|
#include <frostbite2D/graphics/types.h>
|
||||||
|
#include <frostbite2D/graphics/texture.h>
|
||||||
|
|
||||||
namespace ns_game {
|
namespace ns_game {
|
||||||
|
|
||||||
@@ -13,6 +15,25 @@ void drawRect(const frostbite2D::Rect& rect, const frostbite2D::Color& color) {
|
|||||||
frostbite2D::Renderer::get().drawQuad(rect, color);
|
frostbite2D::Renderer::get().drawQuad(rect, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawTexture(const frostbite2D::Rect& bounds,
|
||||||
|
const frostbite2D::Rect& sourceRect,
|
||||||
|
frostbite2D::Ptr<frostbite2D::Texture> texture,
|
||||||
|
const frostbite2D::Color& tint) {
|
||||||
|
frostbite2D::Renderer& renderer = frostbite2D::Renderer::get();
|
||||||
|
frostbite2D::Quad quad = frostbite2D::Quad::createTextured(
|
||||||
|
bounds, sourceRect,
|
||||||
|
frostbite2D::Vec2(static_cast<float>(texture->getWidth()),
|
||||||
|
static_cast<float>(texture->getHeight())),
|
||||||
|
tint.r, tint.g, tint.b, tint.a, renderer.shouldShrinkSubTextureUVs());
|
||||||
|
auto* shader = renderer.getShaderManager().getShader("sprite");
|
||||||
|
if (shader) {
|
||||||
|
shader->use();
|
||||||
|
}
|
||||||
|
renderer.getBatch().submitQuad(quad, frostbite2D::Transform2D::identity(),
|
||||||
|
texture, shader,
|
||||||
|
frostbite2D::BlendMode::Normal);
|
||||||
|
}
|
||||||
|
|
||||||
float worldWidthOf(const LevelDefinition& level) {
|
float worldWidthOf(const LevelDefinition& level) {
|
||||||
return level.collision.levelRight - level.collision.levelLeft;
|
return level.collision.levelRight - level.collision.levelLeft;
|
||||||
}
|
}
|
||||||
@@ -63,7 +84,7 @@ void StageRenderer::drawLayer(const LevelDefinition& level,
|
|||||||
frostbite2D::Rect bounds(rect.bounds.left() + parallaxOffset.x,
|
frostbite2D::Rect bounds(rect.bounds.left() + parallaxOffset.x,
|
||||||
rect.bounds.top() + parallaxOffset.y,
|
rect.bounds.top() + parallaxOffset.y,
|
||||||
rect.bounds.width(), rect.bounds.height());
|
rect.bounds.width(), rect.bounds.height());
|
||||||
drawRect(bounds, rect.color);
|
drawStageRect(rect, bounds);
|
||||||
if (layer.role == StageLayerRole::LevelVisual) {
|
if (layer.role == StageLayerRole::LevelVisual) {
|
||||||
drawRect(frostbite2D::Rect(bounds.left(), bounds.top(), bounds.width(),
|
drawRect(frostbite2D::Rect(bounds.left(), bounds.top(), bounds.width(),
|
||||||
4.0f),
|
4.0f),
|
||||||
@@ -72,4 +93,46 @@ void StageRenderer::drawLayer(const LevelDefinition& level,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StageRenderer::drawStageRect(const StageRect& rect,
|
||||||
|
const frostbite2D::Rect& bounds) const {
|
||||||
|
if (!rect.source.hasTexture()) {
|
||||||
|
drawRect(bounds, rect.color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frostbite2D::Ptr<frostbite2D::Texture> texture =
|
||||||
|
getTexture(rect.source.texturePath);
|
||||||
|
if (!texture) {
|
||||||
|
drawRect(bounds, rect.color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rect.source.useSourceRect) {
|
||||||
|
drawTexture(bounds, rect.source.sourceRect, texture, rect.source.tint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTexture(bounds,
|
||||||
|
frostbite2D::Rect(0.0f, 0.0f,
|
||||||
|
static_cast<float>(texture->getWidth()),
|
||||||
|
static_cast<float>(texture->getHeight())),
|
||||||
|
texture, rect.source.tint);
|
||||||
|
}
|
||||||
|
|
||||||
|
frostbite2D::Ptr<frostbite2D::Texture>
|
||||||
|
StageRenderer::getTexture(const std::string& texturePath) const {
|
||||||
|
const auto existing = textureCache_.find(texturePath);
|
||||||
|
if (existing != textureCache_.end()) {
|
||||||
|
return existing->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
frostbite2D::Ptr<frostbite2D::Texture> texture =
|
||||||
|
frostbite2D::Texture::loadFromFile(texturePath);
|
||||||
|
if (texture) {
|
||||||
|
texture->setSampling(frostbite2D::TextureSampling::PixelArt);
|
||||||
|
}
|
||||||
|
textureCache_[texturePath] = texture;
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ns_game
|
} // namespace ns_game
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
#include "../data/level_definition.h"
|
#include "../data/level_definition.h"
|
||||||
#include "../core/debug_flags.h"
|
#include "../core/debug_flags.h"
|
||||||
|
|
||||||
|
#include <frostbite2D/graphics/texture.h>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace ns_game {
|
namespace ns_game {
|
||||||
|
|
||||||
class StageRenderer {
|
class StageRenderer {
|
||||||
@@ -13,6 +17,13 @@ private:
|
|||||||
void drawBackground(const LevelDefinition& level) const;
|
void drawBackground(const LevelDefinition& level) const;
|
||||||
void drawGrid(const LevelDefinition& level) const;
|
void drawGrid(const LevelDefinition& level) const;
|
||||||
void drawLayer(const LevelDefinition& level, const StageLayer& layer) const;
|
void drawLayer(const LevelDefinition& level, const StageLayer& layer) const;
|
||||||
|
void drawStageRect(const StageRect& rect,
|
||||||
|
const frostbite2D::Rect& bounds) const;
|
||||||
|
frostbite2D::Ptr<frostbite2D::Texture>
|
||||||
|
getTexture(const std::string& texturePath) const;
|
||||||
|
|
||||||
|
mutable std::unordered_map<std::string, frostbite2D::Ptr<frostbite2D::Texture>>
|
||||||
|
textureCache_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ns_game
|
} // namespace ns_game
|
||||||
|
|||||||
Reference in New Issue
Block a user