引言

前置阅读:建议先阅读向量三角函数

引擎语境:本文主要以 Unity / C# 视角讲解,物理更新建议放在 FixedUpdate 中。

运动学 (Kinematics) 关注的是物体如何移动,而不关心为什么移动(那是动力学的事)。 在游戏中,这通常意味着根据速度和加速度更新物体的位置。 换句话说,本篇就是把“速度、加速度”这些物理课里的概念,翻译成一帧一帧更新角色位置的代码。

1. 基础公式:速度与加速度

在经典力学中,如果加速度 \(a\) 近似恒定,则在时间 \(t\) 内: \[ v = v_0 + a t \] \[ s = v_0 t + \frac{1}{2} a t^2 \]

在离散时间步长 \(\Delta t\) 的游戏循环中,我们用近似公式来更新: \[ Velocity_{new} = Velocity_{old} + Acceleration \times \Delta t \] \[ Position_{new} = Position_{old} + Velocity \times \Delta t \]

这里: - \(Velocity\) 表示当前速度向量(位置每秒变化量)。 - \(Acceleration\) 表示每秒速度的变化量(如重力、推力)。 - \(\Delta t\) 通常使用 Time.fixedDeltaTime,保证物理更新稳定。

2. 积分器 (Integrators)

在计算机中,时间不是连续的,而是离散的帧 (\(\Delta t\))。我们需要用数值积分来近似计算位置。

Integration Methods

显式欧拉积分 (Explicit Euler)

这是最简单也是最常用的方法(如 Unity 的 Rigidbody 默认行为)。

void Update(float dt) {
  velocity += acceleration * dt;
  position += velocity * dt;
}
  • 优点: 极易实现。
  • 缺点: 精度低,能量不守恒。随着时间推移,误差会累积(例如:绕地球卫星会逐渐飞离轨道)。

韦尔莱积分 (Verlet Integration)

这种方法不显式存储速度,而是通过当前位置和上一帧位置来推导速度。 \[ x_{new} = 2x_{current} - x_{old} + a \cdot \Delta t^2 \]

void Update(float dt) {
  Vector3 temp = position;
  position = 2 * position - oldPosition + acceleration * dt * dt;
  oldPosition = temp;
}
  • 优点: 非常稳定,特别适合模拟布料、绳索和布娃娃系统 (Ragdolls)。它能自动保持约束条件。
  • 缺点: 处理变长帧率 (Variable Time Step) 比较麻烦,通常需要固定的 FixedUpdate

其他积分器简介

方法 精度 性能 典型应用
显式欧拉 一阶 极快 简单角色移动
半隐式欧拉 一阶(更稳定) 极快 Unity Rigidbody 默认
中点法 (Midpoint) 二阶 需要更高精度的物理
RK4 (Runge-Kutta 4) 四阶 较慢(每步4次求值) 航天模拟、精密轨道
Verlet 二阶 布料、绳索、粒子

半隐式欧拉 (Symplectic Euler) 是游戏中最常用的积分器——它只是把”先更新速度再更新位置”的顺序换了一下,但稳定性比显式欧拉好很多:

// 半隐式欧拉(推荐)
velocity += acceleration * dt;  // 先更新速度
position += velocity * dt;      // 再用新速度更新位置

对于绝大多数游戏来说,半隐式欧拉 + 固定时间步长就足够了。只有当你需要长时间高精度模拟(如行星轨道)时,才需要考虑 RK4。

3. 抛物线运动 (Projectile Motion)

在射击游戏中,我们经常需要预测炮弹的落点。 如果不考虑空气阻力,水平方向是匀速运动,垂直方向是匀加速运动(重力)。

\[ x(t) = x_0 + v_{x0} t \] \[ y(t) = y_0 + v_{y0} t - \frac{1}{2} g t^2 \]

瞄准算法

已知起点 \(P\) 和目标 \(T\),以及炮弹速度 \(v\),求发射角度 \(\theta\)。 这是一个经典的物理问题。通过消除时间变量 \(t\),我们可以得到一个关于 \(\tan \theta\) 的一元二次方程。

代码实现:

float? CalculateLaunchAngle(Vector3 start, Vector3 target, float v, float g = 9.81f) {
  float x = Vector3.Distance(new Vector3(start.x, 0, start.z), new Vector3(target.x, 0, target.z));
  float y = target.y - start.y;
  
  float v2 = v * v;
  float v4 = v2 * v2;
  float term = v4 - g * (g * x * x + 2 * y * v2);
  
  if (term < 0) return null; // 目标太远,打不到
  
  // 通常有两个解:高抛和低抛。这里返回低抛角度(直射)。
  float tanTheta = (v2 - Mathf.Sqrt(term)) / (g * x);
  return Mathf.Atan(tanTheta) * Mathf.Rad2Deg;
}

总结

  • 对于简单的角色移动,欧拉积分足够了。
  • 对于绳索、布料或高精度物理,考虑使用韦尔莱积分
  • 永远使用 Time.fixedDeltaTime 进行物理模拟,以保证确定性。

< 上一篇: 相交检测 | 回到目录 | 下一篇: 碰撞响应 >

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。