本节摘要:把接触点、法向、冲量、AABB、速度、岛编号画进场景,把醒岛数、候选对数、求解时间画成数字。只读绘制,不改 lambda。抖动看点是否闪,穿透看深度颜色,睡眠看岛是否该醒。没有这套眼睛,第 3、4 章的参数全是盲调。
阅读完本节,你应当能够:
世界空间:
屏幕空间:
这些数据在 step 末尾拷到调试快照,渲染线程只读快照。不要在绘制时调用求解器。
DebugContact: p, n, depth, jn, jt DebugBody: aabb, islandId, flags, v
| 看见的现象 | 含义 | 去哪修 |
|---|---|---|
| 接触点每帧换位置 | 流形匹配失败 | 3.3 |
| 法向每帧翻面 | SAT 轴振荡 | 3.2 |
| 冲量线闪而点不闪 | 热启动丢失或 lambda 清零 | 4.2 |
| AABB 不包住形状 | 旋转 AABB 算小 | 3.1 |
| 岛颜色全场景一块 | 静体并进图了 | 6.2 |
| 速度箭头在睡着时仍动 | 睡眠标志没跳过积分 | 1.1、6.2 |
点选一个刚体,冻结它或施加冲量。单步 step。放慢时间(仍用固定 dt,只是少喂累加器)。这比日志强。单步时看接触从无到有:积分后、窄相后、求解后三帧快照能区分是检测错还是求解错。
录视频不够,要录逻辑帧。7.2 的回放能把抖动片断拖回来单步。7.1 负责在单步里看见字段。
💡 关键直觉:物理调试是时间旅行。你要能停在某 tick,看当时所有接触,而不是看平均后的流畅画面——流畅会把抖动藏起来。
能量曲线窗口:对选中岛画动能。睡眠前应下降。上升而无外力,积分或冲量符号错。
绘制接触上千时会比物理还慢。做开关和上限:只画选中岛,或只画屏幕附近。发布版剥离。断言:调试模块不写 BodyHot。代码审查盯这个。
⚠️ 常见坑:在绘制里 normalize 法向并写回流形,发行版不绘制,法向保持未归一,求解爆炸。所有修正发生在 step 内。
统计直方图:每帧接触深度分布。长期有一个很深的尾巴,说明某对穿透修不掉,可能是运动学动画拖着动态体钻墙。
颜色约定全组统一,否则“红”有人当穿透有人当睡眠。写在调试面板图例上。
把 4.2 的金字塔打开全部绘制:四点应稳定,冲量线几乎不动,岛很快变暗。若点在四个角之间跳舞,先别加迭代,先修流形。这是 7.1 存在的理由——它把前面章节的验收变得可看。
文本叠加不要每帧分配字符串。预分配面板。调试自己造成的分配尖峰会误导 6.3 的计时。
着色器里画线用调试线管线,不要把接触点塞进游戏网格。物理调试与玩法渲染分离,避免美术关掉“特效”时你失去眼睛。
工作流:复现抖动 → 打开录制 → 拖到出事前两 tick → 单步积分后看 AABB 是否相交 → 单步窄相后看点是否合理 → 单步求解看速度是否被拉回。三阶段快照用环形缓冲存最近 3 份,内存可接受。不要只存最终态。
计数器实现:原子整数即可,step 末尾交换到只读。字符串面板预分配。不要每帧格式化几十个 sprintf 堆分配。岛颜色用 islandId 哈希到调色板,睡着的降低饱和度。选中刚体高亮其所有接触。
接触过多时采样绘制:只画屏幕视锥附近,或随机抽 200 个加全部选中。抽签必须用确定性 RNG 若你在录制回放,否则回放画面和录制不同会干扰你——虽然哈希不含绘制,心智会乱。回放时绘制用同样采样。
把现象表做成编辑器里的提示按钮:点“点在闪”弹出“去看特征 id 匹配”。这是给未来的自己,也是给同组策划。物理调试不是程序员私有仪式。
能量窗口对选中岛:无外力应下降或走平。上升标红。弹簧演示默认打开,堆箱子默认关,免得重力势能变化吓到人。说明文字写清“动能加弹性势能,不含重力”或含重力,定义要固定。
只存最近三份接触与刚体热列,可接受。不要存全程每 tick 全量除非录制模式。录制另开环形文件。
回放时画面应能对上你的记忆。哈希虽不含绘制,心智会乱。回放共用采样种子。
定义写死并标注。弹簧实验常不含重力,只看弹性加动能。混用会误报上升。图例说明。
现象表做成按钮提示。颜色有图例。物理调试不是私有仪式。开关默认关,热键开。
宏或独立模块不链进发布。还要测:关闭绘制行为不变。这是回归,不是愿望。
误导 6.3 计时。预分配面板。
比物理还慢。视锥或上限加选中。
行为变或仍慢。回归关闭绘制。
误报上升。图例写死定义。
分不清检测错还是求解错。三阶段快照。
审查。交互探测是调试命令,要明确走 step。
红有人当穿透有人当睡眠。统一。
无法单步。逻辑帧才是素材。
「01-调试与可视化」里出现 NaN 要在源头断言:未归一化法向、零长度边当轴、除以过小的有效质量、四元数没归一。把 NaN 传到求解器再查,整场速度都会脏,回溯成本高一个数量级。 本条对应「01-调试与可视化」第 1 号备忘,和相邻条目不要合并成一句空话,分开验收。
和美术、策划交接「01-调试与可视化」参数时,用测试盒而不是形容词。说清楚二十厘米台阶能上、八十厘米不能,比说手感更跟手有用。测试盒还能在引擎升级后当合同跑一遍。 本条对应「01-调试与可视化」第 2 号备忘,和相邻条目不要合并成一句空话,分开验收。
「01-调试与可视化」若跨模块耦合,先单向再双向。布料先撞静地,流体先撞静箱,关节先吊静锚。双向一步到位时,你分不清是 A 推 B 写错还是 B 还力写错。控制变量不是学术,是省生命。 本条对应「01-调试与可视化」第 3 号备忘,和相邻条目不要合并成一句空话,分开验收。
睡眠和唤醒属于「01-调试与可视化」正确性,不只是性能。该醒不醒会穿模,不该醒全醒会卡顿。爆炸查询、关节边、运动学平台都是唤醒图的边。漏一条边,现场像鬼。 本条对应「01-调试与可视化」第 4 号备忘,和相邻条目不要合并成一句空话,分开验收。
「01-调试与可视化」的默认值要进协议版本。改默认迭代次数、默认 dt、默认皮肤半径,旧回放会分叉。版本头拒绝错版本,比静默错乱重放更负责任。 本条对应「01-调试与可视化」第 5 号备忘,和相邻条目不要合并成一句空话,分开验收。
先写 2D 再写 3D 适用于「01-调试与可视化」里所有几何麻烦事。二维金字塔、二维绳、二维铰链能看见点的增减。三维裁剪 bug 极难看,会让你怀疑人生。二维过了再加一根轴。 本条对应「01-调试与可视化」第 6 号备忘,和相邻条目不要合并成一句空话,分开验收。
「01-调试与可视化」不要在热路径调用脚本。求解器迭代十次回调十次,缓存和确定性一起死。标记事件,步末再发。这是布局问题也是架构问题。 本条对应「01-调试与可视化」第 7 号备忘,和相邻条目不要合并成一句空话,分开验收。
对账数字比观感先行。静止支撑冲量是否接近质量乘重力乘 dt,自由落体一秒是否接近四点九米,两球对心是否交换速度。「01-调试与可视化」相关实现先过对账,再谈好看。 本条对应「01-调试与可视化」第 8 号备忘,和相邻条目不要合并成一句空话,分开验收。
「01-调试与可视化」出抖动时按清单:时间步是否固定、流形点是否闪、法向是否跳、恢复系数是否在静止时仍生效、bias 是否过大、迭代是否其实在清零热启动。不要先把摩擦系数调到十。 本条对应「01-调试与可视化」第 9 号备忘,和相邻条目不要合并成一句空话,分开验收。
空间结构必须在世界系。把「01-调试与可视化」绑到摄像机或屏幕格子,逻辑和确定性都会在转视角时崩。渲染剔除不能代替宽相,看不见的物体仍能被撞到。 本条对应「01-调试与可视化」第 10 号备忘,和相邻条目不要合并成一句空话,分开验收。
「01-调试与可视化」的预分配策略:按关卡上限留容量,超出打日志并降级,不要在 step 中途默默扩容。扩容是卡顿尖峰,也是回放时分配顺序不同导致的潜在分叉源。 本条对应「01-调试与可视化」第 11 号备忘,和相邻条目不要合并成一句空话,分开验收。
把「01-调试与可视化」相关的失败做成最小复现:关掉渲染特效、关掉音频、只留固定 dt 的 step 和一份输入日志。能在十秒内重放出来的 bug 才叫被抓住。不能重放就先补录制,再谈修。很多人在这一步省时间,后面用几天陪着偶发抖动。 本条对应「01-调试与可视化」第 12 号备忘,和相邻条目不要合并成一句空话,分开验收。
下一节保证你单步看到的那一帧,在另一台机器上按同一输入还能再走出来。