AEJoy —— 表达式之模拟超越与反弹(五)【JS】

230 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

效果图

2D 弹跳模拟,使用发射角度,初始速度,重力,弹性和摩擦

07.gif

应用于位置、旋转和缩放属性的反弹表达式

08.gif

反弹(Bounce)总览

我们的最终目标是在关键帧运动结束时使用一个模拟反弹的表达式。为了理解弹跳模拟是如何工作的,从一个可能更熟悉的场景开始是很有用的 —— 一个弹射物在击中地面/地板时弹跳。为了简单起见,我们将它限制在二维空间内。当你发射一个 2D 弹丸时,有很多因素会起作用: 重力、物体的弹性、发射角度、初始速度,有时还有摩擦。模拟这个运动的表达式基本上必须将初始速度分解成 x 和 y 分量。重力作用于 y 方向。在每次弹跳时,物体基于弹性失去 y 方向的速度,基于摩擦失去 x 方向的速度。将所有这些因素考虑在内,你便能够获得如下的 2D 反弹表达式:

elev = degreesToRadians(75);
v = 1900;
e = .7;
f = .5;
g = 5000;
nMax = 9;
tLaunch = 1;

vy = v * Math.sin(elev);
vx = v * Math.cos(elev);
if (time >= tLaunch) {
    t = time - tLaunch;
    tCur = 0;
    segDur = 2 * vy / g;
    tNext = segDur;
    d = 0; // x distance traveled
    nb = 0; // number of bounces
    while (tNext < t && nb <= nMax) {
        d += vx * segDur;
        vy *= e;
        vx *= f;
        segDur *= e;
        tCur = tNext;
        tNext += segDur;
        nb++
    }
    if (nb <= nMax) {
        delta = t - tCur;
        x = d + delta * vx;
        y = delta * (vy - g * delta / 2);
    } else {
        x = d;
        y = 0;
    }
    value + [x, -y]
} else
    value

关于这个表达,有几件事需要注意。弹跳参数——发射角(elev)、初速度(v)、弹性(e)、摩擦力(f)和重力(g)——都在表达式的顶部定义。注意,您还必须定义最大弹跳数(nMax),以防止表达式消失在越来越小弹跳的无休止计算中。该表达式的这个版本还包含一个变量来控制启动时间(tLaunch)。

关键帧反弹

现在我们来看看这个表达式,这才是这部分的重点。这个表达式使用属性进入关键帧的速度来计算一个反弹(与进入动画相反的方向),该速度带有一系列递减的反弹。该表达式使用了基本 2D 反弹表达式中的大部分逻辑,除了无需担心发射角度和初始速度(这些是从关键帧中获取的)或摩擦。这个表达式可以用于大多数属性。请参阅上方(第二个)动图,以了解应用于缩放、位置(2D 和 3D)和旋转属性的示例。表达式:

e = .7;
g = 5000;
nMax = 9;

n = 0;
if (numKeys > 0) {
    n = nearestKey(time).index;
    if (key(n).time > time) n--;
}
if (n > 0) {
    t = time - key(n).time;
    v = -velocityAtTime(key(n).time - .001) * e;
    vl = length(v);
    if (value instanceof Array) {
        vu = (vl > 0) ? normalize(v) : [0, 0, 0];
    } else {
        vu = (v < 0) ? -1 : 1;
    }
    tCur = 0;
    segDur = 2 * vl / g;
    tNext = segDur;
    nb = 1; // number of bounces
    while (tNext < t && nb <= nMax) {
        vl *= e;
        segDur *= e;
        tCur = tNext;
        tNext += segDur;
        nb++
    }
    if (nb <= nMax) {
        delta = t - tCur;
        value + vu * delta * (vl - g * delta / 2);
    } else {
        value
    }
} else
    value

和之前一样,您可以通过调整弹性(e)和重力(g)变量来控制弹跳特性。