345 lines
9.9 KiB
Markdown
345 lines
9.9 KiB
Markdown
# Engine Capability Audit
|
||
|
||
目标:后续游戏层尽量复用 Frostbite2D 已有能力,避免在游戏项目里重复造资源、动画、地图、渲染、输入和存档轮子。本文基于当前引擎代码和游戏层现状整理。
|
||
|
||
## 结论
|
||
|
||
当前游戏层已经用了引擎核心框架,并且已把地图、动画、UI、音频、存档和 NPK/SoundPack 资源入口接到真实调用路径。
|
||
|
||
最重要的调整已经完成:场景地图不再以 C++ 硬编码为主,`stage_01.map` 通过 `Asset` / `PvfArchive` / `ScriptParser` 读取入口进入 `LevelDefinition`。
|
||
|
||
当前骨架接入状态:游戏启动时通过 `GameServices` 探测/初始化 `AudioSystem`、`FontManager`、`PvfArchive`、`NpkArchive`、`SoundPackArchive`、`AudioDatabase` 和 `SaveSystem` 状态;`PlayerActor` 已接入 `Animation`/NPK/PNG 优先级;`WhiteboxScene` 已接入 `UIScene`、音频 cue 和 debug 存档。详细状态见 `game_skeleton.md`。
|
||
|
||
推荐方向:
|
||
|
||
```text
|
||
.map 文件
|
||
-> Asset 普通资源读取,或 PvfArchive 从 Script.pvf 内读取
|
||
-> ScriptParser / map parser 解析
|
||
-> 转成 LevelDefinition
|
||
-> StageRenderer / DebugOverlay / WhiteboxScene 继续复用
|
||
```
|
||
|
||
这样地图可以外部维护,素材、图层、碰撞、出生点、camera bounds 和 battle zone 都能从数据文件进入游戏,而不是继续写死在 `whitebox_level.cpp`。
|
||
|
||
## 已经用上的引擎能力
|
||
|
||
### Core / Scene / Actor
|
||
|
||
已使用:
|
||
|
||
- `Application`
|
||
- `SceneManager`
|
||
- `Scene`
|
||
- `Actor`
|
||
- `RefPtr` / `MakePtr`
|
||
|
||
当前状态:
|
||
|
||
- `WhiteboxScene` 继承 `frostbite2D::Scene`。
|
||
- `PlayerActor` 继承 `frostbite2D::Actor`。
|
||
- 场景生命周期、Actor 子节点、Render/Update 流程已接入引擎。
|
||
|
||
保留原则:
|
||
|
||
- 后续敌人、道具、特效仍应优先做成 `Actor` 子类或 Actor 子节点。
|
||
- 不在游戏层另写对象树、生命周期系统或场景管理器。
|
||
|
||
### Graphics / Camera / Render
|
||
|
||
已使用:
|
||
|
||
- `Renderer::drawQuad`
|
||
- `Renderer::drawSprite`
|
||
- `Texture::loadFromFile`
|
||
- `Camera`
|
||
- `RenderStyleProfileId::PixelArt2D`
|
||
- `ResolutionScaleMode::Fit`
|
||
|
||
当前状态:
|
||
|
||
- `StageRenderer` 已复用引擎 Renderer 和 Texture。
|
||
- `CameraController` 使用引擎 `Camera` 做位置控制。
|
||
- `GameUiOverlay` 通过 `UIScene` 进入 UI scene stack。
|
||
- 游戏入口已设置像素风渲染风格。
|
||
|
||
需要调整:
|
||
|
||
- `StageRenderer` 当前自己维护贴图缓存。后续如果引擎已有统一资源缓存或纹理缓存,应迁移过去。
|
||
- 地图层和 tile 层不应该额外造渲染管线,继续输出到引擎 Renderer 即可。
|
||
|
||
### Resource / Asset
|
||
|
||
已部分使用:
|
||
|
||
- `Asset` 被引擎纹理加载间接使用。
|
||
- Windows / Switch 的资源根目录、RomFS 路径已经由引擎 `Asset` 解析。
|
||
|
||
当前状态:
|
||
|
||
- `StageMapResource` 已直接使用 `Asset::readFileToString` 读取 `map/stage_01.map` 或 `assets/map/stage_01.map`。
|
||
- Windows 和 Switch 构建会把 `game/assets/*` 复制到产物 `assets/`,保证 `assets/map/stage_01.map` 路径可用。
|
||
- `stage_01` 主地图已迁移到外部 `.map` 文件。
|
||
- `GameAudio`、`GameServices`、`PlayerAnimationAssets` 都通过 `Asset` 检查可选资源。
|
||
|
||
下一步:
|
||
|
||
- 继续把输入配置、角色动画配置等文本/脚本配置接入 `Asset` 或 PVF。
|
||
|
||
### PVF / ScriptParser
|
||
|
||
引擎已有:
|
||
|
||
- `PvfArchive::open`
|
||
- `PvfArchive::getFileContent`
|
||
- `PvfArchive::getFileBytes`
|
||
- `PvfArchive::getFileRawData`
|
||
- `PvfArchive::resolvePath`
|
||
- `ScriptParser`
|
||
- `ScriptValue`
|
||
|
||
当前状态:
|
||
|
||
- `StageMapResource` 已能在 `PvfArchive` 打开后尝试读取 `map/stage_01.map`。
|
||
- 若资源像 PVF 二进制脚本,会先用 `ScriptParser` 解析出 script values。
|
||
- 若资源是 PVF 文本,会作为普通 `.map` 文本交给 `StageMapLoader`。
|
||
- `ScriptParser` 支持 PVF 脚本二进制格式,不是普通文本 parser。
|
||
|
||
地图结论:
|
||
|
||
- 如果我们的 `.map` 是 PVF 内的二进制脚本格式,就应使用 `PvfArchive + ScriptParser`。
|
||
- 如果我们的 `.map` 是普通文本/JSON/自定义文本格式,就应先用 `Asset` 读取,再由游戏层做最小 parser。
|
||
- 无论哪种格式,最终都应该落到 `LevelDefinition`,不要让 `WhiteboxScene` 直接理解文件格式。
|
||
|
||
### NPK / Sprite / Animation
|
||
|
||
引擎已有:
|
||
|
||
- `NpkArchive`
|
||
- `Sprite::createFromNpk`
|
||
- `Sprite::createFromFile`
|
||
- `Animation`
|
||
- `parseAniFromPvf`
|
||
- `parseAlsFromPvf`
|
||
|
||
当前状态:
|
||
|
||
- 玩家动画加载顺序为 PVF `.ani` -> NPK IMG -> PNG fallback。
|
||
- 如果 PVF 中存在 `character/male/*.ani`,`PlayerActor` 会使用引擎 `Animation`。
|
||
- 如果 NPK 中存在 `sprite/character/male/*.img`,会使用 `Sprite::createFromNpk`。
|
||
- 当前仓库没有正式 PVF/NPK 动画资源,因此运行时仍会回退到 PNG spritesheet。
|
||
|
||
风险:
|
||
|
||
- 只要正式动画资源未进入 PVF/NPK,临时 PNG fallback 仍需要少量手写帧切换。
|
||
|
||
下一步建议:
|
||
|
||
- 为玩家和敌人补齐 PVF `.ani` 或 NPK IMG 后,逐步移除 PNG fallback 的手写帧切换。
|
||
- 将 attack/damage box 从 `Animation::GetCurrentFrameInfo()` 接入战斗 hitbox。
|
||
|
||
### Audio / Save
|
||
|
||
引擎已有:
|
||
|
||
- `AudioSystem`
|
||
- `Sound`
|
||
- `Music`
|
||
- `SoundPackArchive`
|
||
- `AudioDatabase`
|
||
- `SaveSystem`
|
||
|
||
当前状态:
|
||
|
||
- `GameAudio` 已接入 `Sound`、`Music` 和 `AudioDatabase` 查询。
|
||
- `WhiteboxScene` 会尝试播放 `stage_01` BGM 和 battle zone 进入音效。
|
||
- `PlayerActor` 会尝试播放跳跃、攻击音效。
|
||
- `GameSave` 已用 `SaveSystem` 保存 debug overlay 开关。
|
||
|
||
后续原则:
|
||
|
||
- 正式音效、BGM、环境音直接放入资源槽位或 `audio.xml`。
|
||
- 设置、进度继续用 `SaveSystem`,不要在游戏层重新写文件存档服务。
|
||
|
||
### UI / Text
|
||
|
||
引擎已有:
|
||
|
||
- `UIScene`
|
||
- `TextSprite`
|
||
- `FontManager`
|
||
|
||
当前状态:
|
||
|
||
- `GameUiOverlay` 已作为 `UIScene` 接入。
|
||
- 有 `assets/fonts/debug.ttf` 或 `assets/fonts/ui.ttf` 时,状态 UI 使用 `TextSprite`。
|
||
- 缺字体时使用 `Renderer::drawQuad` 色条 fallback,避免 Switch/PC 因字体缺失黑屏。
|
||
|
||
### Event / Input
|
||
|
||
已使用:
|
||
|
||
- `KeyEvent`
|
||
- `EventType::KeyDown`
|
||
|
||
游戏层自定义:
|
||
|
||
- `InputState`
|
||
- `InputStateProvider`
|
||
|
||
判断:
|
||
|
||
- `InputState` 作为游戏语义输入是合理的,能隔离 PC/Switch 差异。
|
||
- 但底层输入事件和生命周期应继续使用引擎事件系统。
|
||
|
||
下一步:
|
||
|
||
- `InputConfig` 可以作为游戏层配置,但读取配置应走 `Asset` 或 PVF。
|
||
- Debug 输入和 gameplay 输入要分离,避免正式版本保留开发快捷键。
|
||
|
||
## 当前游戏层自造/硬编码点
|
||
|
||
### 1. 地图硬编码
|
||
|
||
文件:
|
||
|
||
- `game/src/data/whitebox_level.cpp`
|
||
|
||
当前状态:
|
||
|
||
- 已新增 `game/assets/map/stage_01.map`。
|
||
- 已新增 `StageMapLoader`,把 `.map` 文本解析成 `LevelDefinition`。
|
||
- `CreateWhiteboxLevel()` 已改为优先加载 `.map`,失败时才回退 C++ 白盒数据。
|
||
- C++ 中保留的世界尺寸、平台、视觉层、props、battle zone、spawn point 现在只作为 fallback。
|
||
|
||
后续建议:
|
||
|
||
- 继续扩展 `.map` 的 tileset/tile entry 表达。
|
||
- 逐步把 `level_visual` 的长色块替换成 tile 数据。
|
||
|
||
### 2. PNG fallback 手写动画帧切换
|
||
|
||
文件:
|
||
|
||
- `game/src/actor/player_actor.cpp`
|
||
|
||
当前状态:
|
||
|
||
- 已接入 `Animation` 和 NPK 优先路径。
|
||
- 临时 PNG fallback 仍在 C++ 里硬编码帧数、帧时间和 source rect。
|
||
|
||
建议:
|
||
|
||
- 等正式 `.ani` / `.img` 资源补齐后,删除 PNG fallback 的手写帧切换。
|
||
|
||
### 3. 游戏层贴图缓存
|
||
|
||
文件:
|
||
|
||
- `game/src/stage/stage_renderer.*`
|
||
|
||
问题:
|
||
|
||
- `StageRenderer` 自己维护 `textureCache_`。
|
||
|
||
建议:
|
||
|
||
- 短期可接受,避免每帧重复加载。
|
||
- 如果引擎后续提供统一 Texture cache,迁移到引擎资源缓存。
|
||
|
||
### 4. 调试绘制工具
|
||
|
||
文件:
|
||
|
||
- `game/src/debug/debug_overlay.*`
|
||
|
||
判断:
|
||
|
||
- 这是游戏特定调试可视化,保留合理。
|
||
- 但底层绘制继续用 `Renderer::drawQuad`,不要另造 debug render backend。
|
||
|
||
## `.map` 框架建议
|
||
|
||
### 加载优先级
|
||
|
||
第一阶段已支持普通资源文件:
|
||
|
||
```text
|
||
Asset::readTextFile("map/stage_01.map")
|
||
```
|
||
|
||
第二阶段已预留 PVF:
|
||
|
||
```text
|
||
PvfArchive::getFileContent("map/stage_01.map")
|
||
```
|
||
|
||
如果 `.map` 是 PVF 二进制脚本,则改用:
|
||
|
||
```text
|
||
PvfArchive::getFileRawData("map/stage_01.map")
|
||
ScriptParser parser(rawData, "map/stage_01.map")
|
||
```
|
||
|
||
### 数据流
|
||
|
||
```text
|
||
StageMapLoader
|
||
-> 读取 .map
|
||
-> 解析 world / camera / spawn / layers / collision / zones
|
||
-> 输出 LevelDefinition
|
||
-> WhiteboxScene 使用 LevelDefinition
|
||
```
|
||
|
||
### `.map` 应表达的字段
|
||
|
||
- `world`
|
||
- width
|
||
- height
|
||
- `camera`
|
||
- bounds
|
||
- `player`
|
||
- spawn
|
||
- `layers`
|
||
- id
|
||
- role
|
||
- parallax
|
||
- rect / tile / sprite source
|
||
- `collision`
|
||
- platforms
|
||
- walls
|
||
- bounds
|
||
- `battle_zones`
|
||
- id
|
||
- trigger
|
||
- camera_bounds
|
||
- enemy_spawns
|
||
- `assets`
|
||
- background
|
||
- tileset
|
||
- props
|
||
- effects
|
||
|
||
### 不建议做的事
|
||
|
||
- 不要在 `WhiteboxScene` 里解析 `.map`。
|
||
- 不要绕开 `Asset` 直接用 `std::ifstream` 读资源。
|
||
- 不要继续把地图扩展写死在 `whitebox_level.cpp`。
|
||
- 不要为了地图单独造一套渲染系统。
|
||
|
||
## 推荐执行顺序
|
||
|
||
1. 已完成:`StageMapResource` 定义地图文件路径、普通资源读取和 PVF 读取优先级。
|
||
2. 已完成:`StageMapLoader` 把 `.map` 转成 `LevelDefinition`。
|
||
3. 已完成:`stage_01.map` 把当前 `whitebox_level.cpp` 数据迁移成外部地图文件。
|
||
4. 已完成:`CreateWhiteboxLevel()` 改为加载 `stage_01.map`,失败时回退旧白盒数据。
|
||
5. `StageRenderer`:继续消费 `LevelDefinition`,不直接关心 `.map`。
|
||
6. 文档:把地图编辑流程写入 `scene_acceptance.md` 和场景计划。
|
||
|
||
## 总体原则
|
||
|
||
- 引擎已有的资源、归档、动画、音频、事件、Actor 和 Scene 能力优先使用。
|
||
- 游戏层只做“游戏语义转换”:地图语义、战斗语义、角色状态、关卡流程。
|
||
- 外部资源文件优先通过 `Asset` / `PvfArchive` 进入。
|
||
- C++ 硬编码只作为 fallback,不作为长期地图生产方式。
|