C# 如何使流体阻力方程不依赖于帧率

C# 如何使流体阻力方程不依赖于帧率,c#,xna,xna-4.0,game-physics,C#,Xna,Xna 4.0,Game Physics,我正试图为我开始编写的游戏做计划。(刚开始时非常多) 我的问题是,我希望所有游戏对象的加速/移动部分基于加速力与阻力(从而导致终端速度作为可用速度的上限) 虽然我可以走另一条路,但如果可能的话,我宁愿不走。此外,(一位朋友)建议我可以使用物理库,但这似乎有些过分,而且,我想自己学习和理解这些概念——我总是觉得这样做时我能更好地理解自己的程序 我正在做一个2D游戏,使用Vector2变量表示位置、航向和推力(施加的加速力)。它是自上而下的,所以重力不是方程的一部分 在编写代码之前,我正在Excel

我正试图为我开始编写的游戏做计划。(刚开始时非常多)

我的问题是,我希望所有游戏对象的加速/移动部分基于加速力与阻力(从而导致终端速度作为可用速度的上限)

虽然我可以走另一条路,但如果可能的话,我宁愿不走。此外,(一位朋友)建议我可以使用物理库,但这似乎有些过分,而且,我想自己学习和理解这些概念——我总是觉得这样做时我能更好地理解自己的程序

我正在做一个2D游戏,使用Vector2变量表示位置、航向和推力(施加的加速力)。它是自上而下的,所以重力不是方程的一部分

在编写代码之前,我正在Excel中编写测试用例——这就是我在将数学提交到代码之前检查数学的方式。我发现我对阻力方程的使用使得这个物体的帧率依赖!!具体来说,帧率越高,合成的终端速度越低

我一直在试图根据需要修改方程来解释帧速率,但我没能做到

如果您想使用与我相同的电子表格

但你不必这么做——这里是细节

据我所知,阻力方程是:

Drag = 0.5 * FluidDensity * Velocity * Velocity * DragCoefficient * IncidenceArea
使用从稀薄空气中选取的一些数字进行计算,如果流体密度为0.233,阻力系数为0.4,附带面积为0.1,加速度为每秒50像素,则会发生以下情况:

如果我计算出加速度每0.25秒施加一次(每四分之一秒一次),加速度为加速度力的1/4(与时间匹配),那么我们达到的最终速度约为每秒39.3像素

如果我计算每秒的加速度,我们以每秒53.6像素的速度到达终点

具体地说,每次我计算一个给定的DeltaTime时,得到的速度计算为(代码来自我的头部,而不是IDE,如果其中有错误,我会道歉):

这就是数学的问题。问题是:


该如何表达以使其与帧速率无关?

经典方法是独立于游戏循环帧速率逐步计算模拟物理时间,如果需要,计算每帧多个子迭代以推进物理。这允许您控制时间步长(通常使其小于主帧速率),这也有助于控制其他可能不稳定的计算(如振荡器)。这当然意味着您的物理计算速度必须快于所选固定时间步长的实时计算速度,否则,你的世界将进入慢动作


说到不稳定性,我想您会在当前的实现中看到一些振荡效应,这取决于您在给定的时间步长内是否超过了终端速度。解决此问题的一种方法是通过解析积分计算速度,而不是使用增量步长进行近似计算。要做到这一点,请将您的公式表示为微分方程,并查看它是否具有易于解析求解的形式。

上述代码缺少两部分。虽然我一直在玩“打开”和“关闭”这两个部分来实验性地确定是否需要,但如果没有其他部分,我就很难找到正确的答案

这两个部分是这样的:合成阻力确实需要乘以时间步长,以减少其对加速度的影响,但也许更重要的是——施加在这一帧上的加速度需要在施加到速度之前减去阻力,而不是像我上面所说的那样

修改后的(现在与帧率无关)代码如下所示: 此外,为了简单起见,我将4个“常量”系数减少为一个系数

//In globals / initialization:
Vector2 Position;
Vector2 Speed;
Vector2 ThrustForce;
float Coefficient = 0.009f;
float PreviousDrag = 0.000f;

//In the update loop
//DeltaTime is a float based upon how much of a second passed
Vector2 AccelerationToApply = ThrustForce * DeltaTime + PreviousDrag * DeltaTime;
Vector2 NewSpeed = Speed + AccelerationToApply;
PreviousDrag = Coefficient * NewSpeed * NewSpeed;
Speed = NewSpeed;

通过excel运行此逻辑,我发现无论我计算速度变化的频率(或频率)有多高,在几乎相同的时间,我都会达到相同的近似终端速度。

阻力是一种与推力类似的力,但你不能将其乘以DeltaTime。这闻起来像是一个bug。实际上我也尝试过(这是在Excel中做准备工作的乐趣——我可以快速修改并看到结果),但它仍然给出了截然不同的结果。我整天都在玩Excel,终于找到了正确的答案。你的答案实际上是解决方案的一半,所以作为答案提交,我会接受。同时,我将更新原始帖子并添加完整答案。请不要把你的答案放在问题的主体中。创建并接受你自己的答案。好的,没问题-我现在就这么做。我认为这对我来说不可行。谢谢你的回复,但是。。。我在XNA Update()循环中运行这些计算,玩家可能会以不同的速度运行游戏-这将是多人游戏-这意味着如果我真的走这条路线,我必须一直通过网络发送每个对象的位置,与其发送一个对象是以恒定加速度创建的更新,然后让远程客户端在之后处理它们的位置(直到碰撞时间为止)。@Dracorat的想法是,您将在更新循环中多次更新物理,每个子迭代都有一个固定的时间步长。事实上,这在多人游戏中更为重要,否则不同的帧速率可能会随着时间的推移导致不同的行为,这是由于在每个客户端上随着时间的推移而累积舍入错误。计算固定数量的物理量(比如说每次更新1ms),然后显示当前帧的“最近”状态。因此,如果在对应于t=100.5ms的帧进行渲染,则显示t=1的状态
//In globals / initialization:
Vector2 Position;
Vector2 Speed;
Vector2 ThrustForce;
float Coefficient = 0.009f;
float PreviousDrag = 0.000f;

//In the update loop
//DeltaTime is a float based upon how much of a second passed
Vector2 AccelerationToApply = ThrustForce * DeltaTime + PreviousDrag * DeltaTime;
Vector2 NewSpeed = Speed + AccelerationToApply;
PreviousDrag = Coefficient * NewSpeed * NewSpeed;
Speed = NewSpeed;