为什么整合过度积累?


14

我开始学习如何进行DIY物理学,并且我有一个关于在最基本的层面上实现集成的问题(即,这不是 Euler vs. RK4问题)。

我遇到的几乎每个示例都具有一些integrate()功能,该功能获取自上次更新以来的时间步长,并自上次更新以来更新加速度(和/或速度和/或位置)。

最简单的形式: position += velocity * deltaTime

但是,我不明白为什么通过更改功能就可以很容易地获得这样的积累。例如:getPosition = makeNewFunction()可以返回带有签名的东西Time -> Position,并且该函数的内部工作是通过适当的数学公式生成的。

这样,就没有积累……只要需要获取位置,它就会在当前时间调用该函数。

我的新手理解是,这样做还可以避免由于累积而产生的错误……那为什么不起作用,我还缺少什么?

(之前我确实整理了这个想法的基本概念证明,尽管它同时也在测试其他一些东西,所以它不是最干净的例子:https : //github.com/dakom/ball-bounce-frp

编辑1:如评论中所述,可能很重要的一点是要指出,我还没有学会改变加速度,或者处理急动和其他需要比恒定加速度更高阶积分的事情。

编辑2:这是该思想的一些基本示例代码,以及伪javascript语法-注意,getKinematicPosition由于部分应用,因此它返回的只是时间->位置的新功能:

我会坚持在这里工作,但可能还有其他原因,例如getVelocity,我想...

getKinematicPosition = initialVelocity => acceleration => time => 
  ((.5 *acceleration) * (time * time)) + (initialVelocity * time);

getPosition = getKinematicPosition ([0,0,0]) (GRAVITY);

onTick = totalTime => {
   position = getPosition (totalTime);
   onCollision = () => {
     getPosition = changeTheFunction(totalTime);
     //changeTheFunction uses totalTime to base updates from 0
     //it could use getKinematicPosition or something else entirely
   }
}

1
如果您具有非恒定的速度/加速度,您的功能会做什么?
Linaith '18

我不知道!:D如果那是原因-例如,我还没有改变加速,我真的很感谢能提供更全面的解释,以解决这个问题,因为答案是正确的(否则,我可能会走这条路,走到死路。 !)
davidkomer

6
好吧,如果您的对象只是绕圈转一圈,那么确定...当玩家将其推入盒子时该怎么办?当您调用getPosition(now + 100)时,它会预测未来知道玩家何时停止推动它吗?当您调用getPosition(now-1000)时,是否必须记住过去?
user253751 '18

Answers:


34

...该函数的内部运作是通过适当的数学公式生成的...

这将适用于某些类型的问题,并且搜索的关键词是封闭形式的解决方案。例如,在喀尔巴太空计划中,航天器在轨道上的运动就是以此方式计算的。不幸的是,大多数非平凡的问题(例如所述航天器的大气折返)没有已知的封闭形式的解决方案。因此,需要数学上更简单的数值近似(即integrate()随时间变化)。


啊...太棒了!如果您有时间-您对这种功能方法的想法是什么,然后在我不知道如何使它起作用时重新考虑(例如,如果我正在处理非封闭式表格问题,或者我不知道如何将其变成一个)?我喜欢生成函数的想法,因为它适合数学1:1-但是,如果我总是不可避免地遇到死胡同,那可能不值得...
davidkomer

8
@davidkomer为什么甚至要继续生成函数?如果您能做到这一点,那么您就可以预先计算并记录整个轨迹!当然,人们已经做到了这一点:它称为动画,并且它有一些细微之处。
Joker_vD

功能根据运行时动态而变化...例如,参见FRP弹跳
davidkomer

实际上,我需要将该示例更新为类似于pong的示例,其中移动对象是由用户随机控制的……
davidkomer

10

您的方法存在的问题是,您没有对象的历史记录。如果您朝某个方向移动,则可以计算位置,但是如果撞到某个东西然后反弹,该怎么办?
如果您从最后一个已知的位置进行积累,则可以处理影响并从那里继续前进。如果尝试从一开始就进行计算,则每次都必须重新计算影响,或将其设置为新的开始位置。

您的例子使我想起了赛车游戏。(我不知道位置是否会由物理引擎控制,但是我认为这很有用。)
如果您开车开车,则可以加速和减速。从头到现在,您都无法不知道汽车的速度曲线,就无法计算位置。与存储从开始到现在的每帧速度相比,累积距离要容易得多。

免责声明:到目前为止,我还没有写过游戏物理学,这就是我所看到的问题。

编辑:
在此图中,您可以看到值如何随时间变化。
红色=加速(从开始加速到减速)
绿色=速度(从开始到停止)
蓝色=您的行驶方式。

总速度是从起点到实际记录的加速度的积分。(线与轴之间的区域)
方式是您速度的积分。
如果知道加速度的值,则可以计算其他值。但是,如果我没记错的话,积分也是通过PC上的累积来计算的。而且,存储所有加速度值会增加开销。
另外,可能每一帧都无法计算出来。

加速度/速度/行进时间图

我知道,我的绘画技巧很棒。;)

编辑2:
此示例用于线性运动。倾斜方向使这一点更加困难。


“或将其设置为新的开始位置。” -是的,但是我没有看到问题所在:) Re:汽车示例...我有一种强烈的感觉,我真的需要开始使用像这样的更复杂的东西来直观地了解失败的地方。 。
davidkomer

设置新职位可能不是问题。我编辑了汽车零件
Linaith

1
在汽车游戏中,我认为加速会更加复杂。可能会有跳跃和尖峰,并且可能取决于速度。(例如,当汽车接近最高速度时,加速度会降低为0。)
jpmc26

3
@davidkomer甚至不必理会汽车(除非您愿意),基本的平台游戏都可以。Super Mario Bros中的mario.getPosition(Time)如何工作?
user253751 '18

8

但是,我不明白为什么通过更改函数就可以很容易地获得这样的积累。例如:getPosition = makeNewFunction()可以返回带有Time-> Position签名的东西,并且该函数的内部工作是通过适当的数学公式生成的。

您可以!

使用分析封闭式解决方案进行调用。它具有更精确的优点,因为不存在随时间累积的舍入误差。

但是,仅当您事先知道这种封闭形式时,此方法才有效。对于游戏而言,情况往往并非如此。

播放器的运动不稳定,根本无法放入某些预先计算的功能。玩家可以并且经常改变他的速度和方向。

NPC可能会使用封闭形式的解决方案,而实际上有时会这样做。但是,这还有其他一些缺点。考虑一个简单的赛车游戏。每当您的车辆与其他车辆碰撞时,您都必须更改其功能。也许汽车在地下行驶更快。然后,找到这样的封闭形式的解决方案将非常困难。实际上,可能有更多情况是不可能找到这样的封闭形式,或者过于复杂以至于根本不可行。

使用封闭形式解决方案的一个很好的例子是Kerbal Space Program。一旦您的火箭进入轨道且未受到推力,KSP可能会将其“置于轨道上”。轨道在两体系统中是预先确定的,并且是周期性的。只要火箭不再施加任何推力,您就已经知道了火箭的位置,您可以简单地调用getPositionAtTime(t)它(它的名称不正确,但您可以理解)。

但是,实际上,仅使用逐步集成通常更为实用。但是,当您看到一种存在封闭形式的解决方案并且易于计算的情况时,就去做吧!没有理由使用它。

例如,如果您的角色瞄准大炮,则可以使用封闭形式的解决方案轻松显示大炮弹的预计撞击点。而且,如果您的游戏不允许改变炮弹的路线(例如,没有风),您甚至可以使用它来移动炮弹。请注意,然后,您需要特别注意进入炮弹路径的障碍物。

有很多类似的情况。如果您要构建基于回合的游戏,那么与构建RTS游戏相比,可能会有更多封闭形式的解决方案可用,因为您事先知道所有参数并可以肯定地说它们不会改变(一切不会突然发生)。进入该路径)。

请注意,有一些技术可以解决逐步积分的数值误差。例如,您可以跟踪累积的错误并应用更正项来控制错误,例如 Kahan Summation


8

在只是一个简单的弹跳球的情况下,提出封闭形式的解决方案很容易。但是,更复杂的系统往往需要求解常微分方程(ODE)。 数值解算器需要处理除最简单的情况以外的所有情况。

确实有两类数字ODE求解器:显式和隐式。显式求解器为您的下一个状态提供封闭形式的近似值,而隐式求解器则需要求解方程式。 您为弹跳球所描述的实际上是一个隐式ODE求解器,无论您是否知道!

隐式求解器的优点是能够使用更长的时间步长。对于弹跳球算法,时间步长至少应与下一次碰撞之前的持续时间一样长(这将改变您的功能)。这可以使您的程序运行更快。但是,总的来说,我们不能总是找到我们感兴趣的ODE的好的隐式解决方案。如果不能,我们就只能依靠显式集成。

通过显式集成,我看到的最大优势是众所周知。您可以打开任何60年代的教科书,并阅读所有您需要了解的有关特定集成技术引起的小怪癖的信息。因此,开发人员只需学习一次这些技能,而无需再学习一次。如果您要进行隐式集成,则每个用例都将略有不同,而陷阱也将有所不同。将您从一项任务中学到的知识应用于下一项任务要困难一些。


1

pos(t)= v(t)* t

仅在pos(0)= 0且v(t)= k时有效

您无法在不了解初始条件和整个速度函数的情况下将位置与时间相关联,因此该方程式是积分的近似值

pos(t)= v(t)dt从0到t的积分

编辑_________

这里是每个评论的证明(假设pos(0)= 0)

令v(t)= 4

eqn 1:pos(t)= 4 * t(正确)

eqn 2:pos(t)= c + 4 * t从0到t = 4 * t(正确)

令v(t)= 2 * t

eqn 1:pos(t)= 2 * t ^ 2(错误)

eqn 2:pos(t)= c + t ^ 2从0到t = t ^ 2(正确)

我应该补充一点,您的方程式已经考虑了恒定加速度(即您的方程式为eqn 2,其中v(t)= v0 + a * t且积分的极限为t0和t),因此只要更新,方程式就应该起作用初始位置,初始速度和加速度保持恒定。

编辑2 ________

我还应该补充一点,您还可以使用初始位置,初始速度,初始加速度和恒定加速度来计算位置。换句话说,您可以基于等式2创建一个函数,该函数通过将位置分成时间导数(例如速度,加速度,下一个等等)等来表示位置与时间的关系,但是只有在满足以下条件时,您的方程式才是准确的: v(t)可以用这种方式建模。如果不能仅用速度,加速度,恒定的加速度等来模拟v(t),那么您需要返回到等式2的近似值,当您遇到弹跳,空气阻力,风等因素时,这种情况容易发生。


一个常数。这仅表示v(t)不得随时间变化
Kyy13

我将不得不坐下来,弄清楚为什么它是真的...暂时支持:)我在问题中发布了代码示例,以防万一发生变化
davidkomer

没问题,用更好的单词再次更新:)
Kyy13 '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.