逻辑直线运动的曲线表现模拟1--重力弹跳运动

需求

设计师希望抛射物可以在表现上受重力影响,并希望其拥有包含重力方向速度损耗的弹跳表现。

  1. 起始的抛射高度要相对角色较高
  2. 每次弹跳后的最大纵向高度的下降表现需要相对均匀
  3. 抛射物抵达终点前需要保持弹跳表现
  4. 抛射物抵达终点时,其高度和纵向速度需要尽量接近于0,表现为恰好弹跳到目标位置后停止

实现

前提和已知信息

  1. 前提,抛体运动是一种只受重力影响的运动过程。

  2. 前提,水平面上的速度不会因为碰撞损失。

  3. 这是一个近似过程,而不是模拟。

  4. 目前已知的输入:

    1. 水平面的逻辑直线移动速度horV、加速度a、距离directDis(外部输入)
    2. 重力加速度g(程序内自由设定)
  5. 需要的输出:

    1. 竖直方向初速度initV

输出目的:构建一组最大高度逐渐降低,并使得在最终终点处高度尽量接近0的二次曲线。

简单推导

不考虑加速度和初始高度。

首先,我们需要获得逻辑上的直线运动时间t,这等于曲线运动的时间。t = directDis / horV

然后,我们乐观地假定碰撞时刻的纵向速度损失系数k是一个常数输入,以此开始分情况讨论:

纵向初速度initV

  1. 一次弹跳也不发生,即整个运动过程里只有一条二次曲线,逻辑起点和逻辑终点分别处于曲线的两个零点上。
    1. 因为只有一条二次曲线,所以抛射物运动到曲线最高点、纵向速度为零的时间是t / 2
    2. 显然在这种情况下initV = g * t / 2
  2. 发生一次弹跳,即整个运动过程里有两条二次曲线,在弹跳发生时纵向速度数值减少了k,逻辑起点位于第一条曲线的左零点,逻辑终点位于第二条曲线的右零点。
    1. 因为有两条曲线,所以t等于投射物在两条曲线上运动时间之和,记为t = t1 + t2
    2. 抛射物在第一条曲线上运动时,达到最高点的时间为initV / g,可得t1 = 2 * initV / g
    3. 在抛射物运动到第一条曲线的右零点时,碰撞发生。根据假设,此时抛射物在纵向上损失k的速度,因此第二条曲线的纵向初速度为v2 = initV - k
    4. 与2同理可得,t2 = 2 * (initV - k) / g
    5. 由上可知,t = t1 + t2 = 2 * initV / g + 2 * (initV - k) / ginitV = (g * t / 2 + 1 * k) / (1 + 1)
  3. 发生n次弹跳,整个运动过程里有n+1条二次曲线。
    1. 由前面两次列式,可以发现这实际上是一个多项式推导。
    2. t = 2 * (initV / g + (initV - k) / g + (initV - 2 * k) / g + ... + (initV - n * k) / g)
    3. g * t / 2 = (n + 1) * initV - Σ(n, i = 0) i * k
    4. initV = (g * t / 2 + Σ(n, i = 0) i * k) / (n + 1)
    5. 结束。

通过上方推导得出的纵向初始速度,我们发现等式依然缺少一些参数:kn

弹跳次数n & 纵向速度损失系数k

常识中的物体弹跳会不断损失速度,最终停止弹跳,或静止或在水平面运动。出于需求3,弹跳次数n需要与水平移动时间t正相关,于是我们可以近似地将n赋值为t。可得:

n = t

initV = (g * t / 2 + Σ(Floor(t), i = 0) i * k) / (t + 1)

先前我们假定k是一个常数系数,但在不同的水平移动距离情况下,常数系数无法满足需求4,因此k会是一个与initV正相关、与n反比例相关的表达式。即每次弹跳时,当前速度都会均匀地减少,需要发生的弹跳次数越多,每次弹跳损失的速度越少。可得:

k = 1 / (n + 1)

initV = (g * t / 2 + Σ(Floor(t), i = 0) i / (n + 1)) / (t + 1)

完整的纵向初速度initV表达式就此给出。

在运动过程中,每次发生碰撞时减少的纵向速度diffV为:

diffV = initV / (n + 1)

出于需求2,我们需要额外为diffV寻找一个乘积参数y,该参数需要具备如下特性:

  1. y < 1。防止出现纵向速度在一次碰撞后立刻归零的错误
  2. 一阶导数y' > 0。目的是降低早期弹跳的速度缩减程度,从而实现多个曲线中最大高度下降的相对均匀,顺带扼制多次弹跳中纵向速度过早地归零而导致滚动的趋势
  3. 二阶导数y'' < 0。目的是使y能够向上收敛

于是有一个参数显然地出现了:y = x / (x + 1)。为了实现该参数,我们开始记录目前为止抛射物发生弹跳的次数curBounceCount,该值初始化为1,抛射物每次弹跳时该值+1。至此可得:

diffV = curBounceCount / (curBounceCount + 1) * initV / (n + 1)

结束。