抽完今天第不知道多少根 Mebius 10mg,躺在凉席上刷社区喵。又有人在吵,角色好端端走在走廊里,咚一声嵌进墙里,卡在缝里高频抽搐出不来,只能读档喵。

喵看完只想说,这不合理吧喵。想了想,其实合理得很,物理引擎压根没打算管你喵。

离散检测就是每帧打个卡喵

物理引擎默认的碰撞检测是 Discrete,离散检测喵。工作方式特别像大谷查房:每个物理时间步过来瞄一眼,人不在就不在,中间发生了什么一概不知喵 [S3]。

官方说法是,离散检测按每个物理时间步检查碰撞,是默认模式,计算开销最低;但物理步之间发生的碰撞它可能漏掉,所以快速移动的碰撞基本指望不上它喵 [S3]。

具体到穿模就是隧道效应。上一帧还在墙前面,下一帧已经在墙后面,两次采样点之间隔着整面墙的厚度,两次检查全部合法放行,墙在引擎眼里跟空气没区别喵 [S2][S3]。速度够快,什么墙都拦不住喵。

推测性 CCD 把 AABB 按速度吹大、提前抓潜在触点喵,抓不住的那部分就只能穿过去了喵

推测性 CCD 把 AABB 按速度吹大、提前抓潜在触点喵,抓不住的那部分就只能穿过去了喵 · 图片来源:连续碰撞检测 (CCD),版权归原作者。

想不穿就得加钱喵

官方给了两条路喵 [S3]。

一是提高物理时间步的频率,采样密了自然不容易漏,但物理计算量直接上去,CPU 冒烟喵。二是开连续碰撞检测 CCD,能预测物理步之间可能发生的碰撞,性能开销同样更高喵 [S3]。

CCD 还分两种做法 [S1]。基于扫掠的 CCD 用撞击时间(TOI)算法,沿物体当前速度的前向轨迹扫一圈,算出撞击时间再把物体挪过去;代价是更多 CPU 周期,而且只做线性扫掠,角运动直接忽略,高速旋转的东西照样穿,官方例子是弹球机的弹球杆绕轴转就是碰不到球喵 [S1]。附近高速 CCD 对象一多,开销还会继续涨喵 [S1]。

推测性 CCD 便宜一点,按线性和角运动把粗筛阶段的 AABB 吹大一圈,把下一步所有潜在触点提前塞给求解器喵 [S1]。便宜有便宜的毛病:触点法线不准,高速物体会被不存在的幽灵碰撞绊得莫名跳起来;而且求解和积分阶段不做碰撞检测,物体从求解器拿了太多能量、冲出扩大后的 AABB,照样穿出去喵 [S1]。

所以没有免费的防穿模,只有选哪种姿势多花钱喵。

MERMAID · 图表源代码
flowchart LR
  A["物体移动"] -->|"Discrete 每步打卡"| B{"步与步之间有墙?"}
  B -->|"没有采样点"| C["隧道效应 直接穿墙"]
  A -->|"CCD 扫掠或推测"| D["提前找触点 加钱"]
  D -->|"法线不准 幽灵碰撞"| E["被绊得莫名跳起"]
  D -->|"能量冲出扩大的AABB"| C
  C --> F["卡进墙缝无限抽搐"]

Transform 强推就是往墙缝里硬塞喵

另一类抽搐是纯人为的喵。

Unity 手册写得明明白白:给刚体加力,和直接调 Transform,是完全两种手感;用物理的时候就不该去动同一个物体的 Transform,两边只能选一个,物理开着还硬改 Transform,会搞出碰撞和各种计算上的问题喵 [S2]。

道理不复杂:刚体的速度、受力和求解器是一套,Transform 是另一套坐标喵。绕过 AddForce 直接把坐标怼进墙里,物理引擎下一帧发现两个碰撞体重叠,求解器就拼命把人往外推;推一半,脚本下一帧又把坐标摁回去。推出去、摁回来、再推、再摁,频率拉满,表现出来就是卡在墙缝里高频打摆子,跟触电一样喵 [S2]。

正确姿势要么老老实实走 AddForce 走物理管线,要么把刚体设成 Is Kinematic 让物理别管它,Transform 全权负责喵 [S2]。既要物理碰撞又想自己瞬移,引擎就只能陪你一起抖喵。顺带一提,CCD 只支持球形、胶囊和盒碰撞体的刚体,别的东西连加钱的资格都没有喵 [S4]。

碰撞体定义刚体的物理边界喵,Transform 硬怼进去,求解器就得陪你一起抖喵

碰撞体定义刚体的物理边界喵,Transform 硬怼进去,求解器就得陪你一起抖喵 · 图片来源:Rigidbody,版权归原作者。

喵的现实版本就是大谷堵门收租喵。门框是静态碰撞体,喵是被生活拿 Transform 硬推的刚体。他往前顶,喵往后赖,谁都不肯让出重叠区,喵就在门缝里抖,抖到他叹气为止喵。最后总归是喵下楼拔两根草抵账,穿透深度延迟结算,房子照住喵。

打摆子不丢人喵

所以游戏里卡在墙缝抽成麻花的角色,真不全是程序偷懒:离散采样天生有盲区,CCD 要加钱还有各自的坑,Transform 和 Rigidbody 混用更是官方点名别干的事喵 [S1][S2][S3]。

卡在缝里抖两下怎么了,总比直接穿到地图外面掉进虚空强喵 :sakta_emoji_4:

至于喵自己的手抖,不是求解器数值发散,纯是兜里的 Mebius 抽完了喵 :sakta_emoji_7: 楼下应该还有两根没烧透的烟头,捡一下继续躺喵。喵什么都没做错啊喵。

参考资料

  • [S1] 连续碰撞检测 (CCD) - Unity 手册(读取于 2026-11-05)
  • [S2] Rigidbody - Unity 手册(读取于 2026-11-05)
  • [S3] Discrete collision detection - Unity 手册(读取于 2026-11-05)
  • [S4] Rigidbody.collisionDetectionMode - Unity 脚本 API(读取于 2026-11-05)

参考资料