第 2 篇拆了 step 流程,收尾处说过:高速穿墙不是求解器的锅,是离散积分的采样问题。这篇展开——先给精确的判据,再对比两个引擎的解法。你会发现它们的思路几乎相反:一个事后钳位移,一个事前预生成接触。
判据:什么时候会隧穿
离散积分每步把 body 从 $x_t$ 直接挪到 $x_{t+1} = x_t + v\,\Delta t$,中间的轨迹不存在。设物体特征尺寸为 $s$(球的直径、盒子的最小边),穿墙条件是:
$$ \|v\| \, \Delta t \;>\; s + d_{\text{wall}} $$
$v\Delta t$ 一步跨过自身尺寸加墙厚。代入数字:子弹 300 m/s、60Hz 物理帧,一步 5 米——别说墙,一层楼都穿过去了。而求解器只看到 $x_t$(没碰)和 $x_{t+1}$(已经在墙另一侧,AABB 都不交了)这两个合法状态,中间穿墙这件事在数据里根本没发生过。所以所有 CCD 方案的本质都是同一个:让"中间状态"以某种形式出现在求解器视野里。
Godot:扫掠 + 钳制
Godot Physics 提供两档连续碰撞(PhysicsServer3D::CCDMode):
$Bullet.continuous_cd_mode = RigidBody3D.CCD_MODE_CAST_SHAPE # 或 CAST_RAY
机制:积分前,拿预测位移对 broad phase 做一次扫掠查询,找到沿途最早的接触 fraction $t^*$;把位移钳到 $x_t + t^* \cdot v\Delta t$,法向速度按接触处理(消去或按恢复系数反弹)。CAST_RAY 从质心发一条射线,最便宜但只代表质心轨迹——大物体的边缘照样穿;CAST_SHAPE 对整个形状做扫掠,准但贵,一次扫掠相当于若干次窄相求交。
局限也清楚:只处理平动。旋转造成的扫掠(快速旋转的细长杆)不在覆盖范围内;而且钳制是"截断式"的——$t^*$ 之后这段位移直接丢弃,高速连穿两个薄障碍时只有第一个被处理。
Jolt:speculative contact + linear cast 双轨
Jolt 的默认防线是 speculative contact(推测接触):step 前把每个 body 的 AABB 沿预测位移 $v\Delta t$ 膨胀一个 margin,凡是落在这个"预测包络"内的接触提前生成——尽管物体实际还没碰到。这些接触带着负穿透深度进入求解器,迭代时令法向速度满足"本步结束时不穿透"的约束,等于求解器替你把速度钳到恰好停在接触面上。
好处是它和现有求解管线无缝:不截断位移(本步内多次碰撞都能处理)、天然覆盖连续接触(堆叠的箱子一直是 speculative 的受益者)、不引入新的代码路径。代价是接触数变多——每个高速 body 周围一圈"尚未发生"的接触都占约束位,margin(Jolt 的 mSpeculativeContactDistance)设大了白白增加求解成本。
第二道轨是 linear cast(body_set_motion_quality(LinearCast)):真正的扫掠,用保守推进(conservative advancement)沿路径迭代求最早命中。精度高于 speculative(speculative 对高速+深穿透组合可能欠约束),成本高于 speculative,一般只给子弹、炮弹这种正经高速体开。
第四个变量:物理帧率
提 physics/common/physics_ticks_per_second 到 120/144,$\Delta t$ 减半,$v\Delta t$ 同步减半——很多"需要 CCD"的场景直接退回安全区。成本是物理总耗时翻倍(step 次数翻倍),但物理通常不是帧时间大头时这笔账划算。它和 CCD 不互斥:144Hz + speculative 能覆盖绝大多数弹道。
选型决策
| 场景 | 建议 |
|---|---|
| 子弹/炮弹类高速小物体 | Jolt:LinearCast;Godot Physics:CAST_SHAPE(CAST_RAY 只在物体小、速度快时凑合) |
| 一般刚体偶发穿墙 | 先提物理帧率到 120,大概率症状消失 |
| 堆叠/连锁碰撞不稳 | 不是 CCD 问题,回去查 solver 迭代数(第 2 篇) |
| 高速粒子穿墙 | 粒子碰撞是采样式的,思路同 CCD:要么降速度要么提 amount 密度,没有现成开关 |
一个共通的排查提示:穿墙问题的复现率随帧率变化(60Hz 稳穿、30Hz 必穿、144Hz 不穿),那就是采样问题;与帧率无关的穿透,那是 margin/容差设置问题,别走 CCD 这条路。
本文机制描述基于 Godot 4.x servers/physics_3d(CCD 两档为 PhysicsServer3D::CCDMode)与 Jolt 5.x(mSpeculativeContactDistance、EMotionQuality::LinearCast、保守推进)。