木匣子

Web/Game/Programming/Life etc.

有趣的韦尔莱积分法

很多经典的游戏使用欧拉方法移动场景里的物体,例如飞机、坦克、子弹、玛莉奥等等。欧拉方法记录物体的位置和速度,然后在每个帧循环期间把速度累加到位置上,于是游戏里的东西就动起来了。

$$ \\x':=x+v\Delta t \\v':=v+a\Delta t $$

但游戏开发者总是不满足于此,他们总是要找到更快的,更有趣的方法。有人发现了分子运动学里的 Verlet 积分法 是个不错的主意。与欧拉方法不同,Verlet 积分法记录了分子的当前位置和之前的位置,这一特点很巧妙地与游戏的两个帧状态结合到一起。为了知道分子的速度,只要用当前位置减去之前的位置就行了。如果用 \(x^*\) 表示之前的位置,\(x\)表示当前的位置,\(x’\)表示下一帧的位置,则可以用以下公式来移动分子:

$$ \\x':=2x-x^*+a\Delta t^2 \\x^*:=x $$

注意到公式中 \(2x-x^*=x+(x-x^*)\),其中 \((x-x^*)\) 就是隐含的速度。实际上 Verlet 方法是一种高效且稳定的方法。但它的优点远不止这些。试想有大量的分子在运动,我们只要考虑在适当的时候改变它们的位置就行了,至于分子的速度和加速度,都是 Verlet 积分方法隐式处理的。这为交互性提供了很大的便利。

交互性是游戏的一个很重要因素。玩家选中一个物体,把它移动到某个位置。如果是欧拉方法,要考虑玩家给这个物体施加了多大的力才是合理的,无穷大吗?而在 Verlet 方法下,只要把物体的位置改变到玩家想让它去的地方就行了。然后 Verlet 分处理于下的所有事情。

游戏开发者们提出了一个概念叫约束(Constrain),并设计了各种有趣的结构来限制多个 Verlet 分子之间的位置关系。

棒子 Stick

棒子将两个分子连接起来,并约定它们之间的距离,如果太近了,就让他们分离;如果太远了,就让他们靠近;就好像弹簧一样。

结构体 Construct

多个棒子可以组成有趣的结构,例如在2D游戏中,由3个分子和3个棒子组成三角形;在3D中,由4个分子和6个棒子组成四面体;

链 Hinge

把一串的棒子连起来,就变成了链。

其它 Other

当然还有很多玩法:

  • 给分子增加质量因素的话,可以做出不会移动的分子(质量的倒数为0);
  • 使3个分子保持一定角度的约束,可以做出枝干;
  • 使用大量的分子阵列,并用棒子把它们连接起来,可以做出窗帘一样的效果;

演示

在 Github 上有一个叫 Verlet.js 的开源项目展示了一些很酷的效果:基本形状分形树布料蜘蛛网(胆小误入)。有兴趣的话可以阅读一下它的源码,十分通俗易懂。


Verlet 方法在精度与速度之间取得了前所未有的平衡,越来越多游戏使用这种方法来模拟物体效果,以及人物运动。这里有篇非常给力的论文或许可以给你更多的启发: Advanced Character Physics 。此外,Keith Peters 的另一本书 AdvancED ActionScript 3.0 Animation 对 Verlet 积分方法提供了许多实例代码。