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)思路是:
- 为对象在某个轴上记录 AABB 的最小端点与最大端点。
- 按端点排序并沿轴扫描。
- 只有投影区间重叠的对象才可能成为候选对。
- 利用相邻帧对象位置变化通常较小的特性,增量维护排序。
广相宁可产生少量假阳性,也不能漏掉真实可能碰撞的对象对;精确判断留给窄相。
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 和确定性策略使物理系统能够在真实项目中兼顾玩法、性能和同步需求。