diff --git a/game/docs/ARCHITECTURE.md b/game/docs/ARCHITECTURE.md index b3d9a3c..0be5454 100644 --- a/game/docs/ARCHITECTURE.md +++ b/game/docs/ARCHITECTURE.md @@ -17,8 +17,10 @@ game/ ├─ combat/ 碰撞箱、伤害判定和战斗解算类型 ├─ core/ 共享配置、输入快照和资源路径 ├─ data/ 关卡和游戏数据定义 + ├─ debug/ 调试可视化叠层 ├─ movement/ 可复用的移动和碰撞辅助工具 ├─ scene/ 游戏场景和关卡编排 + ├─ stage/ 关卡视觉层渲染 └─ main.cpp 游戏可执行文件入口点 ``` @@ -37,7 +39,9 @@ main.cpp -> PlayerActor 通过 PlatformMover 移动 -> PlayerActor 更新动画 -> WhiteboxScene 更新摄像机 - -> WhiteboxScene 绘制白盒关卡 + -> StageRenderer 绘制关卡视觉层 + -> Actor 渲染 + -> DebugOverlay 绘制调试叠层 ``` ## 模块职责 @@ -47,6 +51,7 @@ main.cpp - `game_config.h` 存储共享的调整常数,如虚拟分辨率、 重力、玩家速度和攻击持续时间 - `asset_paths.h` 存储稳定的运行时资源路径 +- `debug_flags.h` 集中控制开发期调试显示开关 - `input_state.*` 将原始 SDL 键盘状态转换为与引擎无关的 `InputState` 快照。玩家逻辑使用此快照而不是直接读取 SDL @@ -74,7 +79,17 @@ main.cpp `scene` - `WhiteboxScene` 编排当前关卡:加载关卡数据、创建 - 角色、绘制白盒几何和控制摄像机 + 角色、委托关卡渲染、委托调试叠层并控制摄像机 + +`stage` + +- `StageRenderer` 绘制当前关卡视觉层,包括背景、网格、白盒道具和平台 +- 后续 tileset、视差背景和 props 渲染都应该优先接入该模块 + +`debug` + +- `DebugOverlay` 绘制碰撞矩形和玩家包围盒 +- `DebugFlags` 决定哪些调试层启用 `combat` diff --git a/game/docs/GAME_ARCHITECTURE_BOARD.md b/game/docs/GAME_ARCHITECTURE_BOARD.md index d09df14..4e15e4b 100644 --- a/game/docs/GAME_ARCHITECTURE_BOARD.md +++ b/game/docs/GAME_ARCHITECTURE_BOARD.md @@ -121,11 +121,11 @@ mindmap - `core/game_config.h` - `core/asset_paths.h` - `core/input_state.*` +- `core/debug_flags.h` 下一步: - 增加 `GameContext`,统一传递全局服务。 -- 增加 `DebugFlags`,集中管理调试开关。 - 输入层支持键盘和手柄统一映射。 ### 2. Scene @@ -159,10 +159,10 @@ mindmap - `data/level_definition.h` - `data/whitebox_level.cpp` - `movement/platform_world.*` +- `stage/stage_renderer.*` 下一步: -- 增加 `StageRenderer`。 - 增加 `StageLayer` 数据结构。 - 关卡宽度扩展到 2400 到 3200 像素。 - 引入远景/中景/地面三层绘制。 @@ -287,6 +287,7 @@ mindmap 下一步: +- `debug/debug_overlay.*` 已经提供碰撞和玩家包围盒绘制。 - `F1` 切换 debug overlay。 - 绘制平台碰撞。 - 绘制 battle zone。 @@ -378,12 +379,11 @@ mindmap 下一步建议按这个顺序做: -1. `StageRenderer`:让关卡视觉层从 Scene 中独立出来。 -2. `DebugOverlay`:显示碰撞、战斗区域和出生点。 -3. `BattleZone`:做清版推进的核心框架。 -4. `EnemyActor`:做第一个敌人占位。 -5. `CombatSystem`:把 hitbox/hurtbox 真正跑起来。 -6. `SpriteAnimator`:把玩家动画从硬编码改成组件。 +1. `BattleZone`:做清版推进的核心框架。 +2. `EnemyActor`:做第一个敌人占位。 +3. `CombatSystem`:把 hitbox/hurtbox 真正跑起来。 +4. `SpriteAnimator`:把玩家动画从硬编码改成组件。 +5. `StageLayer`:把白盒视觉层扩展为背景、tiles 和 props。 ## 素材协作提示 diff --git a/game/src/core/debug_flags.h b/game/src/core/debug_flags.h new file mode 100644 index 0000000..b9aaf2c --- /dev/null +++ b/game/src/core/debug_flags.h @@ -0,0 +1,11 @@ +#pragma once + +namespace ns_game { + +struct DebugFlags { + bool showStageGrid = true; + bool showCollision = true; + bool showActorBounds = true; +}; + +} // namespace ns_game diff --git a/game/src/debug/debug_overlay.cpp b/game/src/debug/debug_overlay.cpp new file mode 100644 index 0000000..e65e212 --- /dev/null +++ b/game/src/debug/debug_overlay.cpp @@ -0,0 +1,49 @@ +#include "debug_overlay.h" + +#include + +namespace ns_game { + +namespace { + +void drawRect(const frostbite2D::Rect& rect, const frostbite2D::Color& color) { + frostbite2D::Renderer::get().drawQuad(rect, color); +} + +void drawOutline(const frostbite2D::Rect& rect, const frostbite2D::Color& color, + float thickness = 2.0f) { + drawRect(frostbite2D::Rect(rect.left(), rect.top(), rect.width(), thickness), + color); + drawRect(frostbite2D::Rect(rect.left(), rect.bottom() - thickness, + rect.width(), thickness), + color); + drawRect(frostbite2D::Rect(rect.left(), rect.top(), thickness, rect.height()), + color); + drawRect(frostbite2D::Rect(rect.right() - thickness, rect.top(), thickness, + rect.height()), + color); +} + +} // namespace + +void DebugOverlay::Render(const LevelDefinition& level, const DebugFlags& flags, + const frostbite2D::Rect* playerBounds) const { + if (flags.showCollision) { + drawCollision(level); + } + if (flags.showActorBounds && playerBounds) { + drawActorBounds(*playerBounds); + } +} + +void DebugOverlay::drawCollision(const LevelDefinition& level) const { + for (const auto& platform : level.collision.platforms) { + drawOutline(platform, frostbite2D::Color(0.1f, 0.95f, 0.45f, 0.85f)); + } +} + +void DebugOverlay::drawActorBounds(const frostbite2D::Rect& bounds) const { + drawOutline(bounds, frostbite2D::Color(1.0f, 0.78f, 0.1f, 0.9f)); +} + +} // namespace ns_game diff --git a/game/src/debug/debug_overlay.h b/game/src/debug/debug_overlay.h new file mode 100644 index 0000000..7376960 --- /dev/null +++ b/game/src/debug/debug_overlay.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../core/debug_flags.h" +#include "../data/level_definition.h" + +#include + +namespace ns_game { + +class DebugOverlay { +public: + void Render(const LevelDefinition& level, const DebugFlags& flags, + const frostbite2D::Rect* playerBounds) const; + +private: + void drawCollision(const LevelDefinition& level) const; + void drawActorBounds(const frostbite2D::Rect& bounds) const; +}; + +} // namespace ns_game diff --git a/game/src/scene/whitebox_scene.cpp b/game/src/scene/whitebox_scene.cpp index 38d255f..4364048 100644 --- a/game/src/scene/whitebox_scene.cpp +++ b/game/src/scene/whitebox_scene.cpp @@ -9,14 +9,6 @@ namespace ns_game { -namespace { - -void drawRect(const frostbite2D::Rect& rect, const frostbite2D::Color& color) { - frostbite2D::Renderer::get().drawQuad(rect, color); -} - -} // namespace - void WhiteboxScene::onEnter() { frostbite2D::Scene::onEnter(); level_ = CreateWhiteboxLevel(); @@ -32,35 +24,11 @@ void WhiteboxScene::OnUpdate(float deltaTime) { } void WhiteboxScene::Render() { - drawWhitebox(); + stageRenderer_.Render(level_, debugFlags_); frostbite2D::Scene::Render(); -} - -void WhiteboxScene::drawWhitebox() const { - const float worldWidth = level_.collision.levelRight - level_.collision.levelLeft; - drawRect(frostbite2D::Rect(level_.collision.levelLeft, 0.0f, worldWidth, - config::kVirtualHeight), - frostbite2D::Color(0.10f, 0.12f, 0.14f, 1.0f)); - - for (float x = level_.collision.levelLeft; x <= level_.collision.levelRight; - x += 80.0f) { - drawRect(frostbite2D::Rect(x, 0.0f, 1.0f, config::kVirtualHeight), - frostbite2D::Color(0.20f, 0.22f, 0.25f, 1.0f)); - } - for (float y = 0.0f; y <= config::kVirtualHeight; y += 80.0f) { - drawRect(frostbite2D::Rect(level_.collision.levelLeft, y, worldWidth, 1.0f), - frostbite2D::Color(0.20f, 0.22f, 0.25f, 1.0f)); - } - - for (const auto& prop : level_.props) { - drawRect(prop.bounds, prop.color); - } - - for (const auto& platform : level_.collision.platforms) { - drawRect(platform, frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f)); - drawRect(frostbite2D::Rect(platform.left(), platform.top(), platform.width(), 4.0f), - frostbite2D::Color(0.91f, 0.95f, 0.98f, 1.0f)); - } + const frostbite2D::Rect playerBounds = + player_ ? player_->GetWorldBounds() : frostbite2D::Rect::Zero(); + debugOverlay_.Render(level_, debugFlags_, player_ ? &playerBounds : nullptr); } void WhiteboxScene::updateCamera() { diff --git a/game/src/scene/whitebox_scene.h b/game/src/scene/whitebox_scene.h index e9ee2cb..6ed8d76 100644 --- a/game/src/scene/whitebox_scene.h +++ b/game/src/scene/whitebox_scene.h @@ -1,7 +1,10 @@ #pragma once #include "../actor/player_actor.h" +#include "../core/debug_flags.h" #include "../data/level_definition.h" +#include "../debug/debug_overlay.h" +#include "../stage/stage_renderer.h" #include #include @@ -15,10 +18,12 @@ public: void Render() override; private: - void drawWhitebox() const; void updateCamera(); LevelDefinition level_; + DebugFlags debugFlags_; + StageRenderer stageRenderer_; + DebugOverlay debugOverlay_; frostbite2D::Ptr player_; }; diff --git a/game/src/stage/stage_renderer.cpp b/game/src/stage/stage_renderer.cpp new file mode 100644 index 0000000..3069369 --- /dev/null +++ b/game/src/stage/stage_renderer.cpp @@ -0,0 +1,65 @@ +#include "stage_renderer.h" + +#include "../core/game_config.h" + +#include + +namespace ns_game { + +namespace { + +void drawRect(const frostbite2D::Rect& rect, const frostbite2D::Color& color) { + frostbite2D::Renderer::get().drawQuad(rect, color); +} + +float worldWidthOf(const LevelDefinition& level) { + return level.collision.levelRight - level.collision.levelLeft; +} + +} // namespace + +void StageRenderer::Render(const LevelDefinition& level, + const DebugFlags& debugFlags) const { + drawBackground(level); + if (debugFlags.showStageGrid) { + drawGrid(level); + } + drawProps(level); + drawPlatforms(level); +} + +void StageRenderer::drawBackground(const LevelDefinition& level) const { + drawRect(frostbite2D::Rect(level.collision.levelLeft, 0.0f, + worldWidthOf(level), config::kVirtualHeight), + frostbite2D::Color(0.10f, 0.12f, 0.14f, 1.0f)); +} + +void StageRenderer::drawGrid(const LevelDefinition& level) const { + const float worldWidth = worldWidthOf(level); + for (float x = level.collision.levelLeft; x <= level.collision.levelRight; + x += 80.0f) { + drawRect(frostbite2D::Rect(x, 0.0f, 1.0f, config::kVirtualHeight), + frostbite2D::Color(0.20f, 0.22f, 0.25f, 1.0f)); + } + for (float y = 0.0f; y <= config::kVirtualHeight; y += 80.0f) { + drawRect(frostbite2D::Rect(level.collision.levelLeft, y, worldWidth, 1.0f), + frostbite2D::Color(0.20f, 0.22f, 0.25f, 1.0f)); + } +} + +void StageRenderer::drawProps(const LevelDefinition& level) const { + for (const auto& prop : level.props) { + drawRect(prop.bounds, prop.color); + } +} + +void StageRenderer::drawPlatforms(const LevelDefinition& level) const { + for (const auto& platform : level.collision.platforms) { + drawRect(platform, frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f)); + drawRect(frostbite2D::Rect(platform.left(), platform.top(), + platform.width(), 4.0f), + frostbite2D::Color(0.91f, 0.95f, 0.98f, 1.0f)); + } +} + +} // namespace ns_game diff --git a/game/src/stage/stage_renderer.h b/game/src/stage/stage_renderer.h new file mode 100644 index 0000000..44dbd82 --- /dev/null +++ b/game/src/stage/stage_renderer.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../data/level_definition.h" +#include "../core/debug_flags.h" + +namespace ns_game { + +class StageRenderer { +public: + void Render(const LevelDefinition& level, const DebugFlags& debugFlags) const; + +private: + void drawBackground(const LevelDefinition& level) const; + void drawGrid(const LevelDefinition& level) const; + void drawProps(const LevelDefinition& level) const; + void drawPlatforms(const LevelDefinition& level) const; +}; + +} // namespace ns_game