渲染层笔记

渲染层笔记

核心文件:

  • include/lab/app/ClientRender.h
  • src/app/ClientRender.cpp
  • apps/client_main.cpp

渲染层不是项目的同步核心,但它负责把预测世界展示出来,并把回滚、hash mismatch 等调试信息显示给开发者。


1. RenderCtx

RenderCtx 保存 SDL 渲染资源:

字段 作用
SDL_Window* window 窗口
SDL_Renderer* renderer 渲染器
TTF_Font* font HUD 字体
widthheight 窗口尺寸

初始化:

1
InitRenderer()

释放:

1
ShutdownRenderer()

注意:

SDL 资源要成对创建和释放。后续如果继续完善,可以把 window、renderer、font 封装成 RAII 对象。


2. RenderFrame 输入

渲染函数只接收:

1
RenderFrame(renderCtx, worldSnapshot, rollbackCount, hashMismatchCount)

这说明渲染层不直接修改世界。

它只读 WorldSnapshot,然后画:

  • 迷宫墙体
  • 玩家方块
  • 子弹
  • HUD 文本

这是一个很好的边界:

1
2
3
模拟层产出状态;
渲染层消费状态;
渲染层不反向修改模拟。

3. 世界坐标到屏幕坐标

项目用简单的顶视角坐标转换:

1
2
screenX = windowWidth / 2 + worldX * scale
screenY = windowHeight / 2 - worldY * scale

Y 方向取反,是因为屏幕坐标通常向下为正,而游戏世界里向上为正。

scale 根据迷宫尺寸和窗口大小计算:

1
scale = min(width / mazeWidth, height / mazeHeight) * 0.8

这样不同迷宫尺寸都能大致居中显示。


4. HUD 调试信息

HUD 显示:

  • 当前 tick
  • rollback 次数
  • hash mismatch 次数
  • maze seed
  • 每个玩家 HP
  • 每个玩家 action
  • stateTimer

这些信息是联机同步调试里很关键的观察点。

尤其是:

  • rollback 次数持续上升,说明预测和权威经常偏离。
  • hash mismatch 出现,说明状态还原或确定性有问题。
  • tick/HP/action 可以帮助定位是哪一帧开始分叉。

5. 渲染和模拟的关系

客户端每个 tick 会先推进 worldPred,然后 RenderFrame 画当前预测快照。

收到服务端 State 后,客户端会:

1
2
3
Restore(auth)
Replay(...)
RenderFrame(...)

所以屏幕上看到的是“经过本地预测和权威校正后的世界”。

注意:

渲染只展示预测结果。真正权威状态仍然在服务端。


6. 渲染层注意事项

  • 渲染层不要保存会影响模拟的状态。
  • 渲染层不要修改 WorldSnapshot
  • HUD 是调试工具,不应该参与同步逻辑。
  • 如果增加插值/平滑,最好只做视觉层修正,不要改权威模拟状态。
  • 字体路径当前写死为 /System/Library/Fonts/Menlo.ttc,跨平台需要改成配置项。
  • 如果窗口卡顿,不应该让模拟用真实大 dt 推进,固定帧 accumulator 已经负责处理。
  • 后续可以补充摄像机、缩放、玩家标识、命中反馈,但仍应保持渲染和模拟解耦。