为什么在物理模拟中存在无效的“dt”值?

我使用了一些NeHe的spring代码,在得到了一些非常奇怪的结果之后,我最终意识到了错误的来源 – 我的Update函数中的“dt”值; 所有东西都乘上的值加快/减慢计算,希望根据帧率。 例如:

public void Update(GameTime gt) { float dt = gt.ElapsedGameTime.Milliseconds / 160.0f; velocity += (force / mass) * dt; position += velocity * dt; } 

160.0f对于玩家的更新function似乎很好,但是对于我的弹簧模拟,我需要一个大约3000的值,否则我的弹簧几乎立即就位于(NaN,NaN)。

为什么不好的价值观导致一切变得如此疯狂? 我认为这只会减慢或加速我的模拟,但似乎会造成一些奇怪的级联失败。

编辑:对不起,忘了链接到NeHe的这篇文章: http : //nehe.gamedev.net/tutorial/introduction_to_physical_simulations/18005/

当你做position += velocity * dt你使用position += velocity * dt的新值,而更准确的计算应该使用整个过去的帧持续时间的平均值 。 看到我写这篇文章如何影响轨迹计算。

在你的具体情况下,如果force是一个常量,使用Verlet集成将会给你准确的轨迹,无论帧率如何。 如果Verlet积分还不够,例如,由于引入摩擦,可以使用更高阶的积分方法,比如4阶Runge-Kutta。

我认为你正在使用gt.ElapsedGameTime.Milliseconds那么你应该使用gt.ElapsedGameTime.TotalMilliseconds 。 不知道这是否有影响,但这是一个潜在的错误。

要提高精度(如果这是问题),您可以切换到ElapsedGameTime.Ticks是100-ns间隔,并为fp中间计算加倍,看看是否有帮助。

那么,这是明确的欧拉集成,有史以来最简单和最愚蠢的集成商。 如果Dt是可变的或大的,就很容易引起你的模拟。 你可以做的是切换到另一个:

Verlet / Velocity Verlet

中点欧拉 http://en.wikipedia.org/wiki/Semi-implicit_Euler_method

隐式欧拉 http://www.physics.udel.edu/~jim/Ordinary%20Differential%20Equations/The%20Implicit%20Euler%20Method.pdf

预测校正方法

龙格库塔 。

为什么使用它们(另一个gamedev后)这些好一点,但不容易实现。 你也可能想看看适应性的时间步方法 ..这是与你的问题非常相关(例如可变的帧率可能会引起抖动模拟)。

我相信你的时间步调应该被调整为:

 float dt = gt.ElapsedGameTime.Milliseconds / 1000.0f; 

既然你希望dt是以秒为单位的增量时间(所以一个1.0的dt就意味着1秒过去了)。

如果这不起作用,那么很可能您用来计算ElapsedGameTime的计时器不够准确(Windows上毫秒定时器的常见问题,其分辨率为16毫秒)。

如果gt.ElapsedGameTime.Milliseconds的值始终为0或16,则需要更精确的计时器。