约 24 分钟技术学习游戏引擎

Games104学习笔记(12)

GAMES104:游戏引擎中物理系统的基础理论和算法

第 10 讲|主题:Physics System — Basic Concepts
视频时长:约 2 小时 7 分 15 秒
说明:本文根据本地视频课件与讲解脉络整理,不是逐字稿。

一句话理解

游戏物理系统的目标不是逐项复刻真实世界,而是在固定的实时预算内,稳定地让物体具有可信的运动、碰撞和交互。它将碰撞形状与刚体属性输入数值积分器更新状态,先用广相过滤候选对,再以窄相得到接触信息,最后由约束/冲量求解器修正速度和姿态;射线检测、碰撞分组、休眠、连续碰撞和确定性策略,则让这条管线能服务于实际游戏逻辑。

本讲学习目标

  • 了解静态、动态、运动学刚体与 Trigger 在游戏中的角色分工。
  • 区分球、胶囊、盒、凸体、三角网格与高度场等常用碰撞形状。
  • 掌握力、冲量、质量、摩擦、恢复系数和刚体状态的基本含义。
  • 理解显式欧拉、隐式欧拉与半隐式欧拉的稳定性取舍。
  • 建立线性运动与旋转刚体运动的对应关系。
  • 说明广相、窄相、Minkowski 差、GJK 和 SAT 分别解决什么问题。
  • 理解接触点、法线、穿透深度如何进入约束求解与碰撞响应。
  • 了解 Raycast、碰撞分组、休眠、连续碰撞和确定性模拟的工程价值。

1. 游戏物理系统到底负责什么

游戏物理是 Gameplay、动画、渲染之外的一套状态演化系统。它既要回答“物体下一帧在哪里”,也要回答“哪些对象接触、能否穿过、受到碰撞后如何变化”。

在游戏中,物理通常承担:

  • 角色、载具、可破坏物和抛射物的运动与碰撞。
  • 地形、墙体、道具、机关与角色之间的空间阻挡。
  • 触发区域的进入、离开与停留事件。
  • 射线、扫掠等快速空间查询,用于交互、武器命中、脚底探测和 AI 感知。
  • 对动画或 IK 的接触补充,例如脚底地面高度、可攀爬点和手部交互位置。

它不是把美术网格直接丢进一个“真实世界模拟器”。物理引擎使用为计算而设计的简化表示,并在真实感、可控性、性能、稳定性与可重复性之间取舍。

2. 物理 Actor 与交互类型

2.1 Static、Dynamic 与 Kinematic

游戏世界中的碰撞对象常可分为三类:

类型 是否受力/冲量影响 是否由物理积分移动 常见用途
Static 否 否 地面、建筑、静态障碍物
Dynamic 是 是 箱子、球、碎片、可推动物体
Kinematic 通常不受物理驱动 由游戏逻辑直接指定 电梯、移动平台、脚本控制物体

Dynamic Actor 将质量、速度、外力和碰撞响应交给物理系统。Static Actor 只提供不可动的边界。Kinematic Actor 虽然也能移动,却由动画、脚本或角色控制器直接决定位置,因此必须谨慎处理它与 Dynamic Actor 的接触关系。

2.2 为什么 Kinematic Actor 容易出问题

运动学对象可以在一个时间步内直接改变位置,不必遵守力和冲量带来的连续运动。若它移动过快或与动态物体发生深度穿插,求解器只能在有限迭代中补救,可能造成:

  • 动态物体被突然挤飞或穿透。
  • 移动平台上的物体无法稳定继承平台速度。
  • 角色控制器与物理对象之间出现抖动、卡住或穿墙。

因此,运动学对象虽然方便 Gameplay 控制,但仍需要合理速度、碰撞形状、接触规则和必要的连续检测支持。

2.3 Trigger:检测事件,而非产生阻挡

Trigger 通常是只检测重叠关系的碰撞体:角色进入、离开或停留时触发事件,但不会像实体墙壁一样施加物理响应。

常见用途包括:

  • 区域提示、任务触发和关卡入口。
  • 拾取物、伤害区域和检测范围。
  • 相机切换区、音乐区与环境效果区。

它将“是否发生空间重叠”的信息交给 Gameplay,而不把它误当成“必须模拟力学接触”的问题。

3. 碰撞形状:用足够简单的几何服务交互

3.1 形状选择的原则

渲染网格追求视觉细节,碰撞形状追求计算效率与稳定接触。两者通常不应完全等同。物理形状的选择应围绕:

  • 是否需要精确贴合外观,还是只需大致阻挡?
  • 对象是静态、动态还是查询对象?
  • 它会与多少其他对象接触?
  • 是否需要连续碰撞或高频查询?

越复杂的形状通常越接近外观,但窄相检测、接触生成与求解成本也越高。

3.2 常用基本体

Sphere

球体只需中心和半径,碰撞检测简单、旋转无关,常用于弹丸、拾取物、近似碰撞体或粗略范围检测。缺点是对人形、长条物体和建筑边界贴合较差。

Capsule

胶囊由线段与半径构成,顶部和底部圆滑,适合角色控制器。相较盒体,胶囊更容易越过小台阶、从墙角滑开,也不容易因旋转角点卡住。

Box

盒体能较好表示家具、箱子、平台和规则建筑。轴对齐盒(AABB)适合广相包围体;有方向盒(OBB)能跟随物体旋转,但相交测试更复杂。

3.3 凸体、三角网格与高度场

Convex Mesh

凸体满足任意两点连线都在形状内部的性质。这个性质使支持映射、GJK、SAT 等算法更易高效工作,因此凸包常用于动态刚体的较复杂近似。

Triangle Mesh

三角网格可以较准确表示静态场景的复杂表面,如建筑、岩石与关卡结构。但它通常不适合作为高频动态物体之间的通用碰撞形状:接触面复杂、计算昂贵、数值稳定性更难控制。

Height Field

高度场将地形表示为规则二维网格上的高度采样,特别适合大面积地面、山丘和赛道。它存储紧凑、查询方便,但只能表达“每个水平位置一个高度”的表面,不能天然表示悬空洞穴或垂直墙面。

形状 优点 局限 典型用途
Sphere 检测快、旋转无关 贴合度低 弹丸、范围、粗略碰撞
Capsule 移动平滑、适合竖直角色 不适合棱角物体 角色控制器
Box 简单、适合规则结构 角点易卡、旋转处理更复杂 箱子、平台、建筑
Convex Mesh 可表达复杂外形,适合凸体算法 不能表达凹陷细节 动态道具、凸包近似
Triangle Mesh 静态细节丰富 动态接触代价高 静态场景
Height Field 大地形高效 表达能力受限 地面、山地、赛道

4. 形状属性:质量、摩擦与弹性

4.1 质量与密度

质量 (m) 决定同一外力下物体的加速度:

$F = ma$

若形状已知体积 (V),可由密度 ρ 推导质量:

$m = \rho V$

游戏中并不一定严格使用现实材料密度;更常见的目标是获得符合玩法的相对重量和响应。例如一个可推动箱子可能需要比视觉上更轻,以让角色操作更顺畅。

4.2 摩擦

摩擦决定接触面上的切向相对速度如何被抑制。摩擦过小,物体会像在冰上滑;摩擦过大,物体容易迅速停住或产生不自然的卡滞。

物理引擎通常将摩擦作为接触约束的一部分求解,而不是每帧随意缩小速度。这样它能与接触法线、法向冲量和质量分布共同决定结果。

4.3 恢复系数

恢复系数(Restitution)描述碰撞后的反弹倾向。它主要影响接触法线方向的相对速度:

  • 接近 0:更像非弹性碰撞,碰后不明显反弹。
  • 较高:保留更多反向速度,呈现弹跳。

恢复系数不是“物体有多硬”的完整物理定义;在实时引擎中,它常是一个便于调节的接触参数,还会受阈值、休眠和求解器设置影响。

5. 力、冲量与粒子动力学

5.1 状态变量

对一个只考虑平移的质点,常见状态包括:

  • 位置 (x(t))。
  • 线速度 (v(t) = dx/dt)。
  • 加速度 (a(t) = dv/dt)。
  • 质量 (m)。

力满足:

$F = ma$

动量为:

$p = mv$

牛顿第二定律也可写为:

$F = \frac{dp}{dt}$

这些关系定义了“力如何持续改变速度,速度又如何持续改变位置”。

5.2 冲量

冲量是力在短时间内的累积:

$J = \int F,dt = \Delta p$

在碰撞、爆炸和瞬间击打中,力持续时间很短,直接以冲量修改速度更方便:

$v_{\mathrm{new}} = v_{\mathrm{old}} + \frac{J}{m}$

冲量是约束求解器处理接触的关键语言:碰撞不必被看作“巨大力的精确积分”,而可被近似为在接触点施加一个使速度满足约束的瞬时变化。

6. 时间积分:连续定律如何落到每一帧

6.1 为什么需要积分器

游戏物理是离散时间模拟。每个固定时间步 Δt,系统已知当前状态与外力,需要估计下一状态。选择不同积分方法,会直接影响稳定性、能量误差、计算成本和可调节性。

对一般动力学系统:

$\dot{x} = v$

$\dot{v} = a(x,v,t) = \frac{F(x,v,t)}{m}$

6.2 显式欧拉(Forward Euler)

显式欧拉只用当前状态计算下一步:

$v_{n+1} = v_n + a(x_n,v_n,t_n),\Delta t$

$x_{n+1} = x_n + v_n,\Delta t$

优点是实现非常简单,当前量已知即可直接更新。缺点是对振荡、弹簧、接触等系统容易引入能量误差,步长较大时可能迅速不稳定。

6.3 隐式欧拉(Backward Euler)

隐式欧拉使用下一时刻的状态:

$v_{n+1} = v_n + a(x_{n+1},v_{n+1},t_{n+1}),\Delta t$

$x_{n+1} = x_n + v_{n+1},\Delta t$

它在许多刚性系统上更稳定,数值能量通常不会像显式欧拉那样轻易爆炸;代价是未知的下一状态出现在公式右侧,常需要解方程或迭代。面对复杂非线性约束时,实现和计算成本都明显提高。

6.4 半隐式欧拉(Semi-implicit Euler)

半隐式欧拉先更新速度,再用新速度更新位置:

$v_{n+1} = v_n + a(x_n,v_n,t_n),\Delta t$

$x_{n+1} = x_n + v_{n+1},\Delta t$

它的成本接近显式欧拉,却通常具有更好的长期稳定性,因此在实时游戏物理中很常见。它不是绝对精确的解,但经常是“稳定性足够、成本可控”的工程选择。

6.5 固定步长与帧率

渲染帧间隔可能波动,而物理若直接使用可变 Δt,积分误差和接触稳定性会随帧率变化。常见做法是以固定时间步运行物理,在单帧内必要时执行多次子步;渲染可对相邻物理状态插值。

固定步长有助于:

  • 限制单次积分误差。
  • 让约束迭代次数和接触行为更可预测。
  • 改善回放、网络同步和确定性模拟的基础条件。

7. 从质点到刚体:还要模拟旋转

7.1 刚体状态

刚体假设对象内部形状不变,但可整体平移和旋转。除线性部分外,还需记录:

  • 朝向 (R) 或四元数 (q)。
  • 角速度 ω。
  • 角加速度 α。
  • 转动惯量张量 (I)。
  • 角动量 (L)。
  • 力矩 τ。

刚体比质点复杂的原因是:同一外力施加在不同位置,可能产生不同的旋转效果。

7.2 线性量与角量的对应

线性运动 旋转运动
位置 (x) 朝向 (R)
线速度 (v) 角速度 ω
质量 (m) 转动惯量 (I)
动量 (p = mv) 角动量 (L = Iω)
力 (F = dp/dt) 力矩 (τ = dL/dt)

转动惯量不是一个固定标量就能完全描述的:物体绕不同轴旋转时,对角加速度的抵抗可能不同。惯量张量还会随物体朝向转换到世界空间,通常概念上写作:

$I_{\mathrm{world}} = R I_{\mathrm{body}}R^{\mathsf{T}}$

7.3 角速度与力矩

角速度方向沿旋转轴,大小表示单位时间的转角(弧度)。力作用在质心之外会产生力矩:

$\tau = r \times F$

其中 (r) 是从质心到施力点的向量。接触点的冲量也会同时改变线速度与角速度,因此撞击物体边缘时,物体通常会旋转,而不是只沿直线移动。

7.4 台球式碰撞的启示

球体碰撞示例展示了接触冲量包含法向与切向分量:

  • 法向冲量阻止两物体继续沿接触法线相互靠近,并可体现恢复系数。
  • 切向冲量与摩擦有关,会改变滑动速度并可能引入或改变自旋。

只计算物体中心速度无法完整表现这种交互;接触点相对于质心的位置和旋转惯量同样会进入求解。

8. 碰撞检测:先快后准

一帧中若有 (n) 个对象,直接对所有对象两两做精确检测需要约 (O(n²)) 次测试。物理引擎通常将碰撞检测分为广相(Broad Phase)与窄相(Narrow Phase)。

8.1 Broad Phase

广相的目标不是精确判断碰撞,而是尽量快速排除明显不可能相交的对象对,输出候选对集合。它常使用对象的 AABB、空间网格、树结构或排序结构。

课件展示的 Sort and Sweep(Sweep and Prune)思路是:

  1. 为对象在某个轴上记录 AABB 的最小端点与最大端点。
  2. 按端点排序并沿轴扫描。
  3. 只有投影区间重叠的对象才可能成为候选对。
  4. 利用相邻帧对象位置变化通常较小的特性,增量维护排序。

广相宁可产生少量假阳性,也不能漏掉真实可能碰撞的对象对;精确判断留给窄相。

8.2 Narrow Phase

窄相对广相候选对做精确的几何测试。除了回答“是否重叠”,它还应生成后续求解需要的接触信息:

  • 接触点或接触点集合(Contact Manifold)。
  • 接触法线(Contact Normal)。
  • 穿透深度(Penetration Depth)。

这些数据决定约束求解器将沿哪个方向施加冲量、需要修正多大相对速度,以及如何稳定地处理接触面。

9. Minkowski 差与 GJK

9.1 Minkowski Sum 与 Difference

对于集合/形状 (A) 与 (B):

  • Minkowski Sum:A ⊕ B = { a + b | a ∈ A, b ∈ B }
  • Minkowski Difference:A ⊖ B = { a - b | a ∈ A, b ∈ B }

两个凸形状相交,当且仅当原点位于它们的 Minkowski Difference 内部。这个转换将“两个形状之间是否相交”转化为“一个由差构成的形状是否包含原点”。

9.2 Support Mapping

凸体算法常使用支持函数:给定方向 (d),返回形状在该方向上最远的点。对 Minkowski Difference 的支持点,可由:

$\operatorname{support}_{A\ominus B}(d) = \operatorname{support}_A(d)-\operatorname{support}_B(-d)$

得到。这样无需显式构造完整的差形状,也能逐步探索与原点相关的区域。

9.3 GJK

GJK(Gilbert–Johnson–Keerthi)利用支持映射迭代构造单纯形:二维中可能是点、线段、三角形,三维中可扩展到四面体。每轮检查当前单纯形与原点的关系:

  • 若某个方向上找不到越过原点的支持点,可判定不相交。
  • 若单纯形包围原点,可判定相交。
  • 否则选择新的搜索方向,继续添加支持点。

GJK 适合凸体相交检测,其性能与支持映射实现密切相关。若需要进一步获得穿透深度和最小分离方向,通常还需结合额外算法(如 EPA)或其他接触生成方法。

10. Separating Axis Theorem(SAT)

分离轴定理(SAT)指出:两个凸形状不相交,当且仅当存在某个轴,使它们投影到该轴上的区间不重叠。这个轴称为分离轴。

对于二维凸多边形,常检查各边法线方向;对于三维凸多面体,候选轴通常包括:

  • 两个形状各自面的法线。
  • 两个形状边方向的叉积。

若所有候选轴上投影都重叠,则形状相交。投影重叠最小的轴还能为接触法线和穿透深度提供重要线索。

SAT 对盒体、多边形等形状很直观,但候选轴数量会随形状特征增加。它与 GJK 并非绝对替代关系:引擎可根据形状种类、接触需求和性能要求选择不同窄相算法。

11. 碰撞响应:把接触转为约束

11.1 接触约束

当窄相发现穿透或即将穿透,物理系统需要阻止物体继续向彼此内部运动。一个基础的法向约束可以理解为:接触点沿法线方向的相对速度不应继续为负。

求解器通过施加法向冲量,修正两物体的线速度与角速度,使接触后的相对速度满足约束。恢复系数可让碰撞后保留一定反弹速度;摩擦约束则处理切向滑动。

11.2 约束求解循环

课件给出的结构可概括为:

当前状态 → 应用外力/积分得到预测状态 → 生成碰撞与其他约束 → 迭代求解冲量 → 更新速度与位置

一个物体同时接触地面、墙壁、其他物体时,约束彼此耦合:修正一个接触点会影响另一个接触点。实时引擎通常采用有限次数的迭代求解,而不是追求一次得到精确解析解。

这解释了两个现象:

  • 迭代次数太少,堆叠物体可能下沉、抖动或弹跳不稳。
  • 迭代次数越多通常越稳定,但 CPU 预算也越高。

11.3 穿透修正与稳定性

离散检测常在对象已经有少量重叠后才发现碰撞。除了修正速度,系统往往还需要做位置层面的穿透修正或偏置,避免对象持续陷入彼此内部。

修正过强会造成可见弹跳或“爆开”,修正过弱则会长期穿透。稳定碰撞并非只取决于一个公式,而是积分步长、接触生成、约束排序、迭代次数、偏置与休眠策略的联合结果。

12. 物理查询、过滤与优化

12.1 Raycast

Raycast 从起点沿方向发射射线,查询与场景形状的首次相交。它通常返回命中距离、位置、法线和对象标识。

典型用途:

  • 武器命中判定和视线检测。
  • 鼠标点选、交互物体选择。
  • 角色脚底向下探测地面。
  • AI 视野遮挡或导航辅助判断。

Raycast 是空间查询,不必让被命中的对象发生物理响应;这使它比构造一个短生命周期的真实弹丸更适合即时命中类玩法。

12.2 Collision Group / Layer

碰撞分组让系统在进入昂贵检测前就过滤不应交互的对象。例如:

  • 玩家与触发器需要查询重叠,但不需要实体阻挡。
  • 两个装饰性碎片之间可能无需碰撞。
  • 弹丸只需检测环境与特定阵营角色。

分组与掩码本质上是对“哪些对象对允许进入物理管线”的声明。正确设计过滤矩阵能显著减少候选对,也是避免玩法规则混乱的重要边界。

12.3 Sleeping

静止或近似静止的动态刚体若每帧继续参与积分、碰撞和约束求解,会消耗大量资源。休眠(Sleeping)会在物体速度、角速度或受力低于阈值且保持一段时间后暂停其模拟;当受到外力、碰撞或显式唤醒时再恢复。

休眠尤其适合大规模堆叠场景。阈值应避免过低导致永不休眠,也要避免过高导致物体尚在可见运动时突然冻结。

13. 连续碰撞检测(CCD)

13.1 离散检测的穿透问题

普通离散碰撞通常只比较离散时间点的形状位置。高速小物体可能在相邻两个物理步之间穿过薄墙:前一帧在墙前,后一帧已在墙后,两端都没有检测到重叠。这类问题通常称为 tunneling。

13.2 Time of Impact

连续碰撞检测试图找出两个移动形状首次相接触的时间(Time of Impact, TOI)。找到 TOI 后,系统可先推进到接触时刻、求解碰撞,再模拟余下的时间。

课件介绍了 Conservative Advancement 的思路:根据当前两物体的距离和相对运动速度估计一个安全推进步长,逐步接近接触点;距离足够小则认为发生碰撞,未接触前继续推进。

CCD 能减少高速穿透,但代价高于离散检测。因此通常只对高速、体积小、后果显著的对象启用,例如子弹、快速抛射物或高速角色部件。

14. 确定性模拟

14.1 什么是确定性

若初始状态、输入序列和时间步完全相同,模拟每次运行都得到相同结果,就称为确定性模拟。它对回放、锁步网络、同步校验和可复现调试非常重要。

14.2 为什么物理可能不确定

即使公式相同,不确定性也可能来自:

  • 浮点运算在不同 CPU、编译器或指令路径上的舍入差异。
  • 多线程任务调度导致约束求解顺序改变。
  • 容器遍历或广相候选对顺序不稳定。
  • 不同帧率、可变时间步或不同迭代次数。

复杂接触系统会放大很小的数值差异:一个堆叠物体的接触顺序稍变,数帧之后可能演化为完全不同的状态。

14.3 提升可重复性的方向

常见工程策略包括:

  • 使用固定时间步,并确保同一输入按相同节奏进入模拟。
  • 固定对象、接触对与约束的处理顺序。
  • 控制并行归并顺序,避免无序浮点累加。
  • 对关键网络玩法使用一致的平台、数值路径或专门的确定性方案。

确定性并不自动等于“更真实”,它是一项为同步与调试付出的工程约束,需要与性能、平台兼容性和玩法需求共同权衡。

15. 物理系统的一帧管线

可将本讲的核心流程收束为:

Gameplay 施加力/设置运动学目标 → 固定步长积分预测状态 → Broad Phase 生成候选对 → Narrow Phase 生成接触 → 约束/冲量迭代求解 → 穿透修正与休眠管理 → 输出变换、触发事件与查询结果

其中,每个阶段的责任不同:

阶段 主要问题
Shape 与 Actor 什么几何参与物理,谁能被物理推动
Integration 在外力作用下如何预测下一状态
Broad Phase 哪些对象对值得进一步检测
Narrow Phase 是否碰撞,以及接触点、法线、穿透深度是什么
Constraint Solver 怎样施加冲量让速度和接触关系满足约束
Query / Filter / Sleep 如何高效服务玩法逻辑并控制计算规模
CCD / Determinism 如何处理高速物体与可重复模拟需求

理解这种分层后,物理问题就不再是笼统的“碰撞不对”:它可能是形状选型、积分稳定性、候选对过滤、接触生成、求解迭代或时间步策略中的任一环节出了偏差。

16. 易混淆概念

概念 正确理解
Trigger 与实体碰撞体 Trigger 报告重叠事件,通常不施加阻挡响应
Kinematic 与 Dynamic Kinematic 由外部逻辑移动;Dynamic 的状态由物理力和冲量演化
力与冲量 力是持续作用;冲量是短时间力的积分,直接改变动量
Broad Phase 与 Narrow Phase 前者快速过滤候选,后者精确生成接触信息
GJK 与 SAT 都可用于凸体碰撞判断,但基于不同几何思路与数据访问方式
接触检测与碰撞响应 检测只告诉“发生了什么”,响应负责修改状态以满足约束
Raycast 与真实弹丸 Raycast 是查询;真实弹丸是持续参与物理与碰撞的对象
离散碰撞与 CCD 离散比较若干时刻;CCD 试图寻找运动过程中的首次接触
稳定性与精确性 实时物理通常优先在预算内保持稳定、可控,而非追求无限精确

小结

  • 游戏物理以简化碰撞形状和有限预算模拟可信交互,而不是逐项复刻现实。
  • Static、Dynamic、Kinematic 与 Trigger 分别承担边界、受力对象、脚本移动对象和事件区域等职责。
  • 半隐式欧拉常以接近显式欧拉的成本换取更好的实时稳定性;固定步长是接触稳定和可重复模拟的重要基础。
  • 刚体旋转引入角速度、力矩、转动惯量和角动量,接触冲量会同时影响平移与旋转。
  • 碰撞检测先由广相减少对象对,再由窄相生成接触点、法线与穿透深度。
  • GJK、SAT 等算法服务凸体窄相;约束求解器再将接触信息转为法向、恢复与摩擦冲量。
  • Raycast、分组过滤、休眠、CCD 和确定性策略使物理系统能够在真实项目中兼顾玩法、性能和同步需求。

参考资料