解释Collision Detection模式的离散与连续差异

解读

国内Unity面试中,这道题常被用来快速判断候选人对物理系统底层行为的掌握深度。面试官真正想听的是:

  1. 你是否能用肉眼可见的现象把“穿模/漏检”和“性能消耗”说清楚;
  2. 你是否知道项目该在什么时候切模式,而不是死记文档。
    答得太浅(只说“连续更准”)会被追问代价;答得太深(直接搬Box2D算法)又容易“炫技翻车”。用“场景+代价+解决方案”三段式最稳妥。

知识点

  1. Discrete(离散检测)

    • 逻辑:每帧只在当前位置做一次重叠判断,不推算运动轨迹
    • 表现:高速小物体容易“穿过”薄碰撞体,即所谓的“Tunneling”。
    • 性能:只跑一次AABB broad-phase,移动端开销最低,是默认选项
  2. Continuous(连续检测)

    • 逻辑:把本帧的线性轨迹扫一遍,用**TOI(Time of Impact)**算法提前找碰撞点,再回滚到接触瞬间。
    • 表现:几乎杜绝Tunneling,适合高速子弹、足球、乒乓球这类“小而快”的对象。
    • 性能:每帧额外跑一次** swept-based CCD**(Unity用Bullet的算法),CPU时间×2~×4移动端发热明显
  3. 切换原则

    • 只给“会穿模”的刚体开Continuous,其余保持Discrete;
    • Continuous DynamicContinuous Speculative只在3D物理里出现,2D只有DiscreteContinuous不要混用术语
    • 墙体/地板永远Discrete,子弹/棒球才用Continuous,这是国内大厂代码规范里的硬性条目

答案

“离散检测只在每帧最后时刻判断一次位置,高速物体会直接穿过薄碰撞体,但性能最低,是移动端的默认选择;连续检测会把本帧的运动轨迹扫一遍,用TOI算法提前找到碰撞点并回滚,几乎不会穿模,代价是CPU时间翻倍。实际项目里,只有子弹、足球这类高速刚体才手动切到Continuous,其余对象保持Discrete,靠分层+物理更新频率来平衡精度与帧率。”

拓展思考

  1. 如果面试官追问“为什么连续检测在2D里反而没有 speculative”,可以答:
    “Unity 2D物理底层是Box2D,官方只实现了线性扫掠CCD,没有旋转扫掠,所以高速旋转的刀轮仍可能漏检,这时需要手动降低固定时间步长增大碰撞体厚度。”

  2. 若被问“热更新项目如何动态改检测模式”,可补充:
    “通过HybridCLRILRuntime反射调用Rigidbody.collisionDetectionMode,但必须在生成阶段加[Preserve],防止裁剪,线上热更只敢把Discrete升Continuous,反向降级会触发物理缓存重建,可能造成1帧抖动。”