调整 LightGame 平台碰撞与门尺寸,修复测试编译

- LightPlatform / ShadowPlatform:移除单向板碰撞逻辑,改为完整 tile 碰撞体
- Door:碰撞箱从 32x64 改为 32x32,与单 tile 素材匹配
- 修复 game_engine_tests 编译:补全 LevelData 初始化字段,增加缺失的 include path
This commit is contained in:
SepComet 2026-06-11 15:27:57 +08:00
parent 48fd7818d7
commit d49aef8c0f
5 changed files with 25 additions and 20 deletions

View File

@ -159,6 +159,8 @@ if(BUILD_TESTING AND NOT USE_FRAMEBUFFER)
target_include_directories(game_engine_tests PRIVATE target_include_directories(game_engine_tests PRIVATE
src/Apps/LightGame/src/engine src/Apps/LightGame/src/engine
src/Apps/LightGame/src/systems src/Apps/LightGame/src/systems
src/Apps/LightGame/src/levels
src/Apps/LightGame/generated
${CORE_INCLUDE_DIRS} ${CORE_INCLUDE_DIRS}
) )

View File

@ -20,7 +20,6 @@
- **Linux x86 编译**:验证代码在 GCC/Clang 下有无警告、CMake 配置是否跨平台、系统 SDL2 依赖是否正确。很多嵌入式工具链的问题在 x86 Linux 上就能提前暴露。 - **Linux x86 编译**:验证代码在 GCC/Clang 下有无警告、CMake 配置是否跨平台、系统 SDL2 依赖是否正确。很多嵌入式工具链的问题在 x86 Linux 上就能提前暴露。
- **ARM 交叉编译**:最终在 IMX6U 上跑。若目标板使用 SDL2则 SDL2 仅作为显示/输入适配层,时间由独立 `Platform::ITimeSource` 提供,核心渲染仍按 CPU framebuffer + 一次性提交设计;如需极简依赖,也保留 `/dev/fb0` 后端作为对照。 - **ARM 交叉编译**:最终在 IMX6U 上跑。若目标板使用 SDL2则 SDL2 仅作为显示/输入适配层,时间由独立 `Platform::ITimeSource` 提供,核心渲染仍按 CPU framebuffer + 一次性提交设计;如需极简依赖,也保留 `/dev/fb0` 后端作为对照。
## 开发规范与性能红线 ## 开发规范与性能红线
IMX6U 运行时性能预算较紧,后续开发必须遵守 `docs/DEVELOPMENT_GUIDELINES.md`。如果目标板使用 SDL2仍然要把 SDL2 限制在平台适配层,核心逻辑和渲染热路径不直接依赖 SDL IMX6U 运行时性能预算较紧,后续开发必须遵守 `docs/DEVELOPMENT_GUIDELINES.md`。如果目标板使用 SDL2仍然要把 SDL2 限制在平台适配层,核心逻辑和渲染热路径不直接依赖 SDL
@ -62,22 +61,26 @@ cmake --build build-win --config Release
``` ```
只构建某个 App 只构建某个 App
```bash ```bash
cmake --build build-win --config Release --target IMX6U-Game cmake --build build-win --config Release --target IMX6U-Game
cmake --build build-win --config Release --target IMX6U-Demo cmake --build build-win --config Release --target IMX6U-Demo
``` ```
构建 `IMX6U-Game` 时会自动重新生成 Tom 的 atlas 头文件;也可以单独执行: 构建 `IMX6U-Game` 时会自动重新生成 Tom 的 atlas 头文件;也可以单独执行:
```bash ```bash
cmake --build build-win --config Release --target GenerateTomAtlasHeader cmake --build build-win --config Release --target GenerateTomAtlasHeader
``` ```
运行: 运行:
```bash ```bash
./build-win/Release/IMX6U-Game.exe ./build-win/Release/IMX6U-Game.exe
``` ```
可选帧率档位: 可选帧率档位:
```bash ```bash
./build-win/Release/IMX6U-Game.exe --fps 30 ./build-win/Release/IMX6U-Game.exe --fps 30
./build-win/Release/IMX6U-Game.exe --fps 45 ./build-win/Release/IMX6U-Game.exe --fps 45
@ -95,23 +98,27 @@ sudo apt-get install libsdl2-dev libsdl2-image-dev cmake g++
``` ```
构建: 构建:
```bash ```bash
cmake -B build-linux . cmake -B build-linux .
cmake --build build-linux cmake --build build-linux
``` ```
只构建某个 App 只构建某个 App
```bash ```bash
cmake --build build-linux --target IMX6U-Game cmake --build build-linux --target IMX6U-Game
cmake --build build-linux --target IMX6U-Demo cmake --build build-linux --target IMX6U-Demo
``` ```
构建 `IMX6U-Game` 时会自动重新生成 Tom 的 atlas 头文件;也可以单独执行: 构建 `IMX6U-Game` 时会自动重新生成 Tom 的 atlas 头文件;也可以单独执行:
```bash ```bash
cmake --build build-linux --target GenerateTomAtlasHeader cmake --build build-linux --target GenerateTomAtlasHeader
``` ```
运行: 运行:
```bash ```bash
./build-linux/IMX6U-Game ./build-linux/IMX6U-Game
``` ```
@ -130,6 +137,7 @@ sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
- **Framebuffer 后端**:作为极简依赖和显示通路对照测试。 - **Framebuffer 后端**:作为极简依赖和显示通路对照测试。
构建Framebuffer 对照后端): 构建Framebuffer 对照后端):
```bash ```bash
cmake -B build-arm-fb \ cmake -B build-arm-fb \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm-linux-gnueabihf.cmake \ -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm-linux-gnueabihf.cmake \
@ -142,6 +150,7 @@ cmake --build build-arm-fb
注意Tom 的 atlas 头文件生成工具是主机侧离线工具,依赖 PC/Linux 主机上的 SDL2_image主机构建 `IMX6U-Game` 时会自动执行ARM 交叉编译过程不会执行它。如果修改了 `src/Apps/Game/assets/raw/` 里的 PNG必须先在 Windows 或 Linux x86 构建目录执行 `GenerateTomAtlasHeader` 或构建一次 `IMX6U-Game`,再进行 ARM 交叉编译。ARM 构建只消费已经生成好的 `src/Apps/Game/generated/tom_atlas.h` 注意Tom 的 atlas 头文件生成工具是主机侧离线工具,依赖 PC/Linux 主机上的 SDL2_image主机构建 `IMX6U-Game` 时会自动执行ARM 交叉编译过程不会执行它。如果修改了 `src/Apps/Game/assets/raw/` 里的 PNG必须先在 Windows 或 Linux x86 构建目录执行 `GenerateTomAtlasHeader` 或构建一次 `IMX6U-Game`,再进行 ARM 交叉编译。ARM 构建只消费已经生成好的 `src/Apps/Game/generated/tom_atlas.h`
构建SDL2 后端,要求工具链/sysroot 可找到目标板 SDL2 开发库): 构建SDL2 后端,要求工具链/sysroot 可找到目标板 SDL2 开发库):
```bash ```bash
cmake -B build-arm-sdl \ cmake -B build-arm-sdl \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm-linux-gnueabihf.cmake \ -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-arm-linux-gnueabihf.cmake \
@ -150,6 +159,7 @@ cmake --build build-arm-sdl
``` ```
部署到开发板: 部署到开发板:
```bash ```bash
scp build-arm-sdl/IMX6U-Game root@imx6u:/tmp/ scp build-arm-sdl/IMX6U-Game root@imx6u:/tmp/
# 或部署 framebuffer 对照版本: # 或部署 framebuffer 对照版本:
@ -157,6 +167,7 @@ scp build-arm-fb/IMX6U-Game root@imx6u:/tmp/
``` ```
板子上运行: 板子上运行:
```bash ```bash
/tmp/IMX6U-Game /tmp/IMX6U-Game
``` ```
@ -315,9 +326,11 @@ IMX6U-Game/
## 模块说明 ## 模块说明
### Draw2D ### Draw2D
- **DrawContext**:统一绘制入口,封装 FrameBuffer、DepthBuffer、Rasterizer、TriangleRasterizer对外提供 `clear`、`clear_color`、`clear_depth`、`draw_line`、`draw_triangle`、`draw_sprite`、`draw_sprite_ex`、`draw_text`、`draw_tilemap`、`fill_rect`、`present` 接口 - **DrawContext**:统一绘制入口,封装 FrameBuffer、DepthBuffer、Rasterizer、TriangleRasterizer对外提供 `clear`、`clear_color`、`clear_depth`、`draw_line`、`draw_triangle`、`draw_sprite`、`draw_sprite_ex`、`draw_text`、`draw_tilemap`、`fill_rect`、`present` 接口
### RenderData ### RenderData
- **Image**:通用图像数据结构,持有 `const void* pixels``PixelFormat`(当前统一 `RGBA5551`),支持 color_key 透明跳过 - **Image**:通用图像数据结构,持有 `const void* pixels``PixelFormat`(当前统一 `RGBA5551`),支持 color_key 透明跳过
- **Sprite**:描述 atlas 中的子区域,通过 `const Image* atlas` 引用源图,是对外 sprite 绘制单位 - **Sprite**:描述 atlas 中的子区域,通过 `const Image* atlas` 引用源图,是对外 sprite 绘制单位
- **Tilemap**:使用 `uint16_t` tile id 引用 atlas 中的固定大小 tile`EmptyTile` (`0xFFFF`) 表示空 tile - **Tilemap**:使用 `uint16_t` tile id 引用 atlas 中的固定大小 tile`EmptyTile` (`0xFFFF`) 表示空 tile
@ -325,20 +338,24 @@ IMX6U-Game/
- **Color**RGBA8888 颜色值用于绘制接口参数和调试sprite 运行时像素格式仍为 RGBA5551 - **Color**RGBA8888 颜色值用于绘制接口参数和调试sprite 运行时像素格式仍为 RGBA5551
### Core ### Core
- **FrameBuffer**CPU 侧 RGB565 颜色缓冲,渲染结果先写在这里 - **FrameBuffer**CPU 侧 RGB565 颜色缓冲,渲染结果先写在这里
- **DepthBuffer**:深度测试用 Z-buffer - **DepthBuffer**:深度测试用 Z-buffer
- **Renderer**:渲染器辅助工具 - **Renderer**:渲染器辅助工具
- **Timer**:整数毫秒固定步长 tick 生成器,支持 30/45/60 FPS 档位和每帧剩余时间计算 - **Timer**:整数毫秒固定步长 tick 生成器,支持 30/45/60 FPS 档位和每帧剩余时间计算
### Math ### Math
- 通用数学类型:`Vector2/3/4`、`Matrix4x4` - 通用数学类型:`Vector2/3/4`、`Matrix4x4`
- 纯头文件实现,无动态分配 - 纯头文件实现,无动态分配
### Rasterizer ### Rasterizer
- **Rasterizer**Bresenham 线段光栅化,入口做快速全屏可见性检查,屏幕内走 `set_pixel_unsafe` 快路径,屏幕外走 Cohen-Sutherland 裁剪 - **Rasterizer**Bresenham 线段光栅化,入口做快速全屏可见性检查,屏幕内走 `set_pixel_unsafe` 快路径,屏幕外走 Cohen-Sutherland 裁剪
- **TriangleRasterizer**:扫描线三角形填充 + 定点深度插值(增量式整数边缘函数,内层循环无 float 运算) - **TriangleRasterizer**:扫描线三角形填充 + 定点深度插值(增量式整数边缘函数,内层循环无 float 运算)
### Platform ### Platform
- **IDisplay**:显示后端抽象,解耦渲染与输出 - **IDisplay**:显示后端抽象,解耦渲染与输出
- **SDLDisplay**SDL2 后端PC 调试和 IMX6U SDL2 目标路径共用这一类适配思想 - **SDLDisplay**SDL2 后端PC 调试和 IMX6U SDL2 目标路径共用这一类适配思想
- **FBDisplay**`/dev/fb0` 对照后端,用于极简显示通路验证 - **FBDisplay**`/dev/fb0` 对照后端,用于极简显示通路验证
@ -366,6 +383,7 @@ IMX6U-Game/
## 当前状态与后续 ## 当前状态与后续
**已完成:** **已完成:**
- 可旋转立方体的 3D 渲染MVP 变换、背面剔除、扫描线填充、深度测试) - 可旋转立方体的 3D 渲染MVP 变换、背面剔除、扫描线填充、深度测试)
- 双平台显示后端SDL2 / Framebuffer - 双平台显示后端SDL2 / Framebuffer
- 离线资源转换工具PNG sprite -> RGBA5551 C++ 头文件,像素字体 -> bitmap atlas/header - 离线资源转换工具PNG sprite -> RGBA5551 C++ 头文件,像素字体 -> bitmap atlas/header
@ -376,6 +394,7 @@ IMX6U-Game/
- CMake 跨平台构建 - CMake 跨平台构建
**待完成(按优先级):** **待完成(按优先级):**
1. FrameBuffer / FBDisplay 性能优化(目标像素格式 backbuffer、dirty rect、专用 tile/sprite 快路径、NEON 1. FrameBuffer / FBDisplay 性能优化(目标像素格式 backbuffer、dirty rect、专用 tile/sprite 快路径、NEON
2. 应用层拆分Launcher / GameA / GameB / Shared和统一 `IApp` 主循环 2. 应用层拆分Launcher / GameA / GameB / Shared和统一 `IApp` 主循环
3. SDL2 输入抽象(键盘/触摸/按键状态快照) 3. SDL2 输入抽象(键盘/触摸/按键状态快照)

View File

@ -84,7 +84,7 @@ namespace LightGame
case GameObjectType::Door: case GameObjectType::Door:
obj.collider = RenderData::BoundingBox2D( obj.collider = RenderData::BoundingBox2D(
Math::Vector2Int(0, 0), Math::Vector2Int(ts, ts * 2)); Math::Vector2Int(0, 0), Math::Vector2Int(ts, ts));
obj.solid = true; obj.solid = true;
obj.light_threshold = LightThreshold(spawn.light_min, spawn.light_max); obj.light_threshold = LightThreshold(spawn.light_min, spawn.light_max);
obj.sprite = &spr_door_closed; obj.sprite = &spr_door_closed;

View File

@ -143,9 +143,6 @@ namespace LightGame
continue; continue;
} }
const bool is_one_way = (other.type == GameObjectType::LightPlatform ||
other.type == GameObjectType::ShadowPlatform);
const RenderData::BoundingBox2D other_world = other.get_world_collider(); const RenderData::BoundingBox2D other_world = other.get_world_collider();
world_collider = obj.get_world_collider(); world_collider = obj.get_world_collider();
@ -154,21 +151,6 @@ namespace LightGame
continue; continue;
} }
if (is_one_way)
{
const int32_t obj_bottom = world_collider.max.y;
const int32_t other_top = other_world.min.y;
const int32_t prev_bottom = obj_bottom - move_y;
if (prev_bottom > other_top || obj.velocity.y <= 0)
{
continue;
}
obj.position.y = other_top - (obj.collider.max.y - obj.collider.min.y);
obj.velocity.y = 0;
}
else
{ {
const int32_t overlap_left = world_collider.max.x - other_world.min.x; const int32_t overlap_left = world_collider.max.x - other_world.min.x;
const int32_t overlap_right = other_world.max.x - world_collider.min.x; const int32_t overlap_right = other_world.max.x - world_collider.min.x;

View File

@ -392,6 +392,8 @@ namespace
0, 0,
spawns, spawns,
2, 2,
nullptr,
0,
Math::Vector2Int(16, 16), Math::Vector2Int(16, 16),
0, 0,
0, 0,