物理学恢复力公式中的数学误差

4

我承认,我是一个数学白痴。我应该在家里尝试吗?可能不行,否则会受重伤。无论如何,任何有游戏和物理经验的人都可以审查这段代码,并提供为什么原型中存在速度“损失”的见解,这将受到极大的钦佩。

概念:演员从天空落下,某个时刻与边界(弹床.y)相交,之后重力(在本例中为1.6)停止操作,反弹因子(在本例中为-6)接管。

现在我正在尝试创建一个零和情况,在这种情况下,您从弹床“弹跳”(hero.y>= trampoline.y)中得到的速度与进入时相同。但是,结果显示这并不是事实。

我尝试了五种不同的方法,这是最新的一种,考虑到在我的游戏计时器的任何给定“tic”(33毫秒)中,我们可能已经越过了弹床的边界,因此加速规则将已更改。我真的以为我做到了,但结果显示每次弹跳后速度都会减小,这不是我预期的。

我向您提交以下代码,希望您进行审查:

var newV:Number;
if (hero.y < trampoline.y) { newV = hero.velocityY + gravityAccelerationInPixels }
else { newV = hero.velocityY + TrampolineActor.SPRING_ACCELERATION }

var newY:Number = hero.y + newV;
trace("Coming in: y=" + hero.y + " oldVelocity=" + hero.velocityY + " newVelocity=" + newV + " Change in V: " + (newV - hero.velocityY) + ". Testing for newY=" + newY);

if ((hero.y < trampoline.y && newY < trampoline.y) || (hero.y >= trampoline.y && newY >= trampoline.y)) {
    hero.y = newY;
    hero.velocityY = newV;
} else {
    trace("SPLIT");

    var percent:Number = (trampoline.y - hero.y) / newV; trace("Percent: " + percent);
    var newVV:Number;
    if (hero.y < trampoline.y) {
        // going down!
        newVV = hero.velocityY + percent * gravityAccelerationInPixels; trace("New velocity before split: " + hero.velocityY + " Change in V: " + (newVV - hero.velocityY));
        newVV += (1 - percent) * TrampolineActor.SPRING_ACCELERATION; trace("Percent after split: " + (1 - percent) + " Change in V: " + (newVV - hero.velocityY));
    } else {
        // movin on up!
        newVV = hero.velocityY + percent * TrampolineActor.SPRING_ACCELERATION; trace("New velocity before split: " + hero.velocityY + " Change in V: " + (newVV - hero.velocityY));
        newVV += (1 - percent) * gravityAccelerationInPixels; trace("Percent after split: " + (1 - percent) + " Change in V: " + (newVV - hero.velocityY));
    }
    trace("New velocity: " + newVV + " Change in V: " + (newVV - hero.velocityY));

    hero.velocityY = newVV;

    hero.y += hero.velocityY;
}

附加信息: 屏幕原点在左上角,所以 y-- = 向上更高 我的tic目前设置为33毫秒 gravityAccelerationInPixels 目前是 1.62 (比较随意) SPRING_ACCELERATION 是任意的6,但我希望调整该数字以控制人物在减速/吸收周期中能距离蹦床多远。
目前我希望有一个解决方案,在人物与蹦床断开联系后,在tic上的速度与进入前的速度相同(但是为负数)。我甚至尝试存储和检索进入位置和速度,但这看起来只是一种干扰。

你是否考虑到了英雄越过蹦床边界时发生的加速度?你的代码似乎假设英雄在蹦床下方时上一个tick恰好在蹦床上。但实际情况并非如此,我认为你会失去一些速度。 - Gabe
谢谢Gabe!我可能错了,但这正是我的代码试图解决的问题。整个底部部分是我意识到在tic时刻,下一步将越过边界。因此,我查看了边界之前的速度作为tic的百分比,然后添加了边界之后剩余的速度变化。整个“百分比”业务是我尝试考虑边界穿越发生的位置(在上一个tic和这个tic之间),然后分配一个百分比,以便我可以计算出更现实的向量。 - Tom Auger
三个问题:1)y是向上还是向下增加,2)您是否介意您的蹦床表现出非物理性质,例如向上的重力而不是弹性,3)您想知道您的代码哪里出了问题,还是想要一个可行的解决方案,或者两者都要? - Beta
Beta,非常感谢你的关注。1)随着y值的减小,屏幕上方的高度会上升(屏幕原点在左上角)。2)这是一个游戏,所以我不在乎我们需要做什么——我已经使用一个常数(6)来模拟吸收和恢复。3)我更想弄清楚我哪里出了问题。 我很愿意从这中学到东西,因为我已经与这段代码斗争了一周了。非常感谢! - Tom Auger
您可能还想调整增量速度(从加速度)和位移(从速度)以反映上一帧渲染完成后到当前帧渲染开始之间实际经过的毫秒数。当帧之间的间隔不完全是33ms时,这将有助于保持事物看起来流畅(Flash的enter frame事件在时间上往往不准确,但某些帧更新所需的时间肯定比其他帧长)。这需要以米(或英尺或其他单位)每秒为单位跟踪速度和加速度。 - Cameron
1个回答

14

以下是针对此类仿真的一些调试技巧:

  • 首先:确保你知道每个变量和常量的单位。例如,如果hero.y是像素距离,那么由于它受到hero.y + newV的影响,newV也必须是像素;但实际上它应该是速度(每秒移动的距离),所以它实际上应该是每帧像素数(因为你是每帧执行一次)。

    接下来的调试步骤中,最好确保所有值都以“每秒”而不是“每帧”表示。为此,引入一个等于每帧秒数的值。然后,你就有了hero.y + newV * timestepnewV = hero.velocityY + gravityAccelerationInPixelsPerSecondSquared * timestep

    可以将gravityAccelerationInPixelsPerSecondSquared * timestep作为一个常数计算出来,但要确保所有硬编码的值都不依赖于你的时间步长,这样你就可以轻松地进行以下尝试:

  • 尝试将你的时间步长减少一定比例k。如果这样做可以减小误差,则说明你的离散时间步长计算与完美的时间积分答案不匹配。如果不能减少误差,则说明你的仿真是一致的,但在某种意义上“非物理”。

  • 尝试计算你系统的总能量。也就是说,计算以下各项之和:

    • 任何移动物体的动能,1/2·m·v²(m为质量,v为速度)
    • 任何自由落体物体的重力势能,m·g·hg为重力加速度常数,h为距离任意参考点的高度)
    • 弹簧的势能为1/2·k·d²(其中k为弹簧常数,希望等于您的“恢复系数”(但是为正数),d为与松弛状态的距离)

    如果您让h=hero.y-trampoline.y,则对于您的特定系统,您有

    E=1/2·m·v²+m·g·max(h,0)+1/2·k·(min(h,0))²

    (从您的代码中可以看出,在人物在蹦床上时不施加重力; 如果施加了,则将max(h,0)更改为h。)

    确保以一致的尺寸执行此操作(无法将像素每秒添加到像素中)和单位(无法将像素每秒添加到每帧像素每秒英寸)。不要忘记平方也会平方单位。

    对于您所述的目标——人物以相同的速度弹起——您具有一个无摩擦的系统,因此系统的能量在弹跳之前、期间和之后应该相同。如果能量改变,则注意何时发生更改。例如:

      如果在角色坠落时加速度发生变化,那么你对于恒定加速度情况下的模拟就是不正确的。一个常见错误(看起来你在你的代码中也犯了这个错误)是忘记了在恒定加速度公式(constant acceleration)下计算位置时需要考虑公式的第三项:newPos = pos + velocity * timestep + 1/2 * acceleration * timestep^2,其中acceleration表示重力加速度。(同样重要的是,在更新速度之前更新位置,以便在位置计算中不使用“明天”的速度与“昨天”的位置。)尝试模拟落到硬表面上而不是弹床,以简化情况。
      如果在人物触碰或离开蹦床时百分比发生变化,则你的percent计算没有达到其目标。

我的问题是否与使用(错误的)欧拉积分有关?http://gafferongames.com/game-physics/integration-basics/ - Tom Auger
@Tom Auger:我的回答中加粗的部分就是关于正确整合的内容。对于这个简单的问题,你可以使用更高级的数值积分技术,但是我所描述的方法是将你的公式修正为精确的闭式版本(在一个小时间间隔内重复应用,以便获得动画效果而不是端点答案)。 - Kevin Reid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接