原理 我们的图形化程序是基于帧来显示动画的。每隔一段时间,屏幕内容更新一次,称为一帧。为了模拟出自由落体的动画,我们的程序需要计算出每帧小球出现的位置。 想象一下用一个照相机,在小球下落过程中,每隔一段等长的时间,对小球的位置拍个照片。经过一段时间后,这些照片上小球的位置,就是我们要计算的数值。 模型 最简单的模型,是小球从静止状态自由下落。将重力加速度记为 $g_0$,我们知道在任意时刻 $t$,小球的速度是 $$v(t)=g_0t=g_0nT$$ $T$ 为相邻两帧的间隔时间。上式中 $g_0$, $T$ 都为常量,我们可以合并为一个新的常量 $g_1$,这样 $v(n)$ 构成了一个离散序列: $$ v(n)=g_1n$$或者$$ v(n)=v(n-1)+g_1 \tag{1}$$ 速度 $v(n)$ 是个等差数列,随时间变化的曲线如下图中的红线: 为了简化问题,假设第 $n$ 帧到第 $n+1$ 帧之间,小球保持 $v(n)$ 匀速,那么在两帧之间走过的路程为 $$\Delta y(n)=v(n)T=g_1nT=gn$$ 这里得到了第 $n$ 帧内,小球走过的路程。而第 $n$ 帧的小球位置$y(n)$ 是前 $n$ 帧内走过路程的累积:$$ y(n) = y(n-1)+ gn \tag{2}$$ 这里 $g$ 是一个常量,和真正的重力加速度的数值 $9.8 m/s^2$已经没有关系,但是仍然起到了加速度的作用:这个值越大,小球下落的越快。位置$y(n)$ 的级差是等差数列,随时间变化的曲线如下图中的红线: 这个曲线近似是抛物线。在连续时间模型下,$y(n)$ 的级差对应路程 $s(t)$n的导数,也就是瞬时速度 $v(t)$. 级差是等差数列,则对应瞬时速度$v(t)$是线性递增函数。 更深入的研究,请参看 蛙跳积分法。 实现 用下面的伪代码可以实现上面的 $(2)$ 式: var y=0, n=0, g=1 while(小球未落地) y+=(n++)*g 这就是自由落体的代码实现。如下图所示: (点击动画重新播放) 落地反弹 如何模拟当小球接触到地面后的反弹? 理想条件下,小球弹起的能量保持不变,速度数值不变,方向变为竖直向上。为了便于计算速度,将上节的下落过程 改写为下面的伪代码: var y=0, v=0, g=1 while(小球未落地) v+=g y+=v 这里 位置 $y$ 和速度 $v$ 是量纲不同的变量,不能直接相加,但这里这么写纯粹为了计算数值,效果和 上节 相同。接下来可以写小球反弹的场景: var y=0, v=0, g=1 while(true) if(小球触碰地面){ v=-v } v+=g y+=v 效果如下图: 考虑损耗 由于摩擦的存在和小球的形变,反弹的一瞬间,有能量的损失,所以速度从数值上会减少。假设减少的数量和速度成正比,则剩余的速度是原速度乘上一个常数,记为 $f$, 同时有 $0