逻辑直线运动的曲线表现模拟1--重力弹跳运动
需求
设计师希望抛射物可以在表现上受重力影响,并希望其拥有包含重力方向速度损耗的弹跳表现。
- 起始的抛射高度要相对角色较高
- 每次弹跳后的最大纵向高度的下降表现需要相对均匀
- 抛射物抵达终点前需要保持弹跳表现
- 抛射物抵达终点时,其高度和纵向速度需要尽量接近于0,表现为恰好弹跳到目标位置后停止
实现
前提和已知信息
前提,抛体运动是一种只受重力影响的运动过程。
前提,水平面上的速度不会因为碰撞损失。
这是一个近似过程,而不是模拟。
目前已知的输入:
- 水平面的逻辑直线移动速度horV、加速度a、距离directDis(外部输入)
- 重力加速度g(程序内自由设定)
需要的输出:
- 竖直方向初速度initV
输出目的:构建一组最大高度逐渐降低,并使得在最终终点处高度尽量接近0的二次曲线。
简单推导
不考虑加速度和初始高度。
首先,我们需要获得逻辑上的直线运动时间t,这等于曲线运动的时间。t = directDis / horV
。
然后,我们乐观地假定碰撞时刻的纵向速度损失系数k是一个常数输入,以此开始分情况讨论:
纵向初速度initV
- 一次弹跳也不发生,即整个运动过程里只有一条二次曲线,逻辑起点和逻辑终点分别处于曲线的两个零点上。
- 因为只有一条二次曲线,所以抛射物运动到曲线最高点、纵向速度为零的时间是
t / 2
。 - 显然在这种情况下
initV = g * t / 2
。
- 因为只有一条二次曲线,所以抛射物运动到曲线最高点、纵向速度为零的时间是
- 发生一次弹跳,即整个运动过程里有两条二次曲线,在弹跳发生时纵向速度数值减少了k,逻辑起点位于第一条曲线的左零点,逻辑终点位于第二条曲线的右零点。
- 因为有两条曲线,所以t等于投射物在两条曲线上运动时间之和,记为
t = t1 + t2
。 - 抛射物在第一条曲线上运动时,达到最高点的时间为
initV / g
,可得t1 = 2 * initV / g
。 - 在抛射物运动到第一条曲线的右零点时,碰撞发生。根据假设,此时抛射物在纵向上损失k的速度,因此第二条曲线的纵向初速度为
v2 = initV - k
。 - 与2同理可得,
t2 = 2 * (initV - k) / g
。 - 由上可知,
t = t1 + t2 = 2 * initV / g + 2 * (initV - k) / g
,initV = (g * t / 2 + 1 * k) / (1 + 1)
。
- 因为有两条曲线,所以t等于投射物在两条曲线上运动时间之和,记为
- 发生n次弹跳,整个运动过程里有n+1条二次曲线。
- 由前面两次列式,可以发现这实际上是一个多项式推导。
t = 2 * (initV / g + (initV - k) / g + (initV - 2 * k) / g + ... + (initV - n * k) / g)
。g * t / 2 = (n + 1) * initV - Σ(n, i = 0) i * k
。initV = (g * t / 2 + Σ(n, i = 0) i * k) / (n + 1)
。- 结束。
通过上方推导得出的纵向初始速度,我们发现等式依然缺少一些参数:k和n。
弹跳次数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,该参数需要具备如下特性:
y < 1
。防止出现纵向速度在一次碰撞后立刻归零的错误- 一阶导数
y' > 0
。目的是降低早期弹跳的速度缩减程度,从而实现多个曲线中最大高度下降的相对均匀,顺带扼制多次弹跳中纵向速度过早地归零而导致滚动的趋势 - 二阶导数
y'' < 0
。目的是使y能够向上收敛
于是有一个参数显然地出现了:y = x / (x + 1)
。为了实现该参数,我们开始记录目前为止抛射物发生弹跳的次数curBounceCount,该值初始化为1,抛射物每次弹跳时该值+1。至此可得:
diffV = curBounceCount / (curBounceCount + 1) * initV / (n + 1)
结束。