第4章 刚体运动学:位置、速度、加速度、角速度、角加速度

好,咱们今天聊聊刚体运动学。说实话,这是整个物理引擎最基础、也最绕不开的一块。你想想看,游戏里一个箱子从桌上掉下来,一辆车在赛道上漂移,甚至角色手里的剑挥出去——这些动作背后,全是刚体在动。

我个人习惯把运动学分成两半来看:线性运动角运动。前者管“往哪走、走多快”,后者管“怎么转、转多快”。两者合在一起,才能描述一个刚体在三维空间里的完整状态。

4.1 位置与速度:最朴素的物理量

先说位置。刚体的位置,说白了就是它质心在空间里的坐标。我们用向量 p 表示,通常是个三维向量 (x, y, z)。在游戏里,每一帧我们都要更新这个位置。

速度呢?就是位置随时间的变化率。公式很简单:

v = dp / dt

但在实际代码里,我们不会真的去求导。我们用离散的增量:

p_new = p_old + v * dt

嗯,这里要注意。这个公式是显式欧拉积分,简单粗暴,但精度一般。我在项目中遇到过一个问题:用这个公式模拟一个弹簧摆,结果能量越算越多,摆锤越荡越高——这就是数值发散。后来我换成了半隐式欧拉,才稳住。

小技巧: 如果你刚开始写物理引擎,先用显式欧拉把逻辑跑通,再考虑换成更稳定的积分器。别一上来就搞 RK4,容易把自己绕晕。

4.2 加速度:力的体现

加速度是速度的变化率。牛顿第二定律告诉我们:

a = F / m

这里 F 是作用在刚体上的合力,m 是质量。加速度本身不直接改变位置,它先改变速度,速度再改变位置。所以更新顺序很重要:

  1. 计算合力 F
  2. 计算加速度 a = F / m
  3. 更新速度 v += a * dt
  4. 更新位置 p += v * dt

你想想看,如果先更新位置再更新速度,那位置用的就是旧速度,物理上就不对了。我曾经调试一个角色跳跃的 bug,跳起来总比预期矮一截,查了半天发现就是更新顺序写反了。

核心要点: 加速度是桥梁,连接了“力”和“运动”。没有加速度,力就是空谈。

4.3 角速度:旋转的灵魂

线性运动讲完了,咱们聊聊旋转。刚体不光会平移,还会转。描述旋转快慢的量叫角速度,用 ω(omega)表示,单位是弧度/秒。

角速度是个向量。方向呢?用右手定则:拇指指向旋转轴,四指弯曲方向就是旋转方向。比如一个轮子绕 Y 轴正转,角速度向量就是 (0, ω, 0)

更新角度(或者说旋转量)的方式和位置类似:

θ_new = θ_old + ω * dt

但这里有个坑。在 3D 里,我们通常用四元数旋转矩阵来表示朝向,而不是简单的角度。直接用欧拉角更新容易遇到万向锁。我建议你一开始就用四元数,虽然理解起来有点绕,但后面省心很多。

// 伪代码:用四元数更新朝向
Quaternion q_old = body.orientation;
Quaternion q_delta = Quaternion(axis, omega * dt);
body.orientation = q_delta * q_old;
body.orientation.normalize(); // 别忘了归一化
注意: 四元数乘法不满足交换律。顺序错了,旋转方向就反了。我刚开始写的时候,把乘数顺序搞反,结果物体往反方向转,调试了整整一个下午。

4.4 角加速度:扭矩的产物

角加速度 α 是角速度的变化率。它由扭矩 τ转动惯量 I 决定:

α = τ / I

这里 I 是刚体绕某轴的转动惯量,可以理解为“旋转方向上的质量”。质量越大越难推,转动惯量越大越难转。

更新流程和线性运动完全对应:

  1. 计算合扭矩 τ
  2. 计算角加速度 α = τ / I
  3. 更新角速度 ω += α * dt
  4. 更新朝向(用四元数或矩阵)

我在项目中遇到过一个问题:一个细长的棍子,绕不同轴的转动惯量差别很大。如果我用一个标量 I 来近似,旋转行为就完全不对。后来我换成了惯性张量(一个 3x3 矩阵),才准确模拟出棍子的旋转。

避坑指南: 对于非对称物体,千万别用标量转动惯量。老老实实算惯性张量,否则旋转看起来会“飘”。

4.5 线性与角运动的对应关系

你看,线性运动和角运动的结构几乎一模一样。我整理了一张表,方便你对照:

概念 线性运动 角运动
位置/朝向 位置向量 p 四元数 q 或旋转矩阵 R
速度 线速度 v 角速度 ω
加速度 线加速度 a 角加速度 α
惯性 质量 m 转动惯量 I
力/扭矩 力 F 扭矩 τ
运动方程 F = m * a τ = I * α

说白了,你只要把线性运动那一套搞懂了,角运动就是换个符号、换个数据结构的事。但要注意,角运动在实现上更复杂,因为旋转不满足交换律,而且朝向的表示方式有好几种。

4.6 实际应用中的注意事项

最后,我分享几个实际开发中的经验:

  • 时间步长要稳定: 固定时间步长比可变步长好。可变步长会导致物理表现不一致,尤其在联机游戏中。
  • 数值稳定性: 角速度更新后,记得对四元数归一化。否则累积误差会让物体变形。
  • 阻尼处理: 真实世界有空气阻力、摩擦等。在引擎里,我习惯给角速度加一个小的阻尼系数,比如 ω *= 0.99,防止旋转永远停不下来。
  • 调试技巧: 把位置、速度、角速度打印出来,或者用 gizmo 在场景里可视化。我曾经靠这个发现了一个角速度方向算反的 bug。
个人建议: 刚开始实现时,先只做 2D 的刚体运动学。2D 里旋转只有一个轴,用标量角度就行,简单很多。跑通了再扩展到 3D。

好了,刚体运动学的基础就这些。下一章我们会聊到碰撞检测,那才是物理引擎里最刺激的部分。做好准备,咱们到时候见。