简单的基于物理的运动

时间:2009-03-20 16:57:37

标签: math 2d physics

我正在研究2D游戏,我正在尝试使用一些基本的物理代码将对象加速到最高速度。

这是它的伪代码:


const float acceleration = 0.02f;
const float friction     = 0.8f;  // value is always 0.0..1.0
      float velocity     = 0;
      float position     = 0;

move()
{
   velocity += acceleration;
   velocity *= friction;
   position += velocity;
}

这是一种非常简化的方法,不依赖于质量或实际摩擦(代码内摩擦只是一种阻止运动的通用力)。它的效果很好,因为“速度* =摩擦力”;部分保持速度不超过某一点。然而,正是这种最高速度及其与加速和摩擦的关系,我有点失落。

我想要做的是设置一个最高速度,以及达到它所需的时间,然后使用它们来获得加速度和摩擦值。

即,


const float max_velocity = 2.0; 
const int   ticks;       = 120; // If my game runs at 60 FPS, I'd like a 
                                // moving object to reach max_velocity in 
                                // exactly 2 seconds.
const float acceleration = ?
const float friction     = ?

6 个答案:

答案 0 :(得分:37)

我发现这个问题非常有趣,因为我最近做了一些关于使用阻力建模弹丸运动的工作。

第1点:您实际上是使用explicit/forward Euler iteration更新位置和速度,其中状态的每个新值都应该是旧值的函数。在这种情况下,您应该先更新位置,然后更新速度。

第2点: the effect of drag friction有更真实的物理模型。一个模型(由Adam Liss建议)涉及与速度成比例的阻力(称为斯托克斯阻力,通常适用于低速情况)。我之前提出的那个涉及一个与力的 square 成比例的阻力(称为二次阻力,通常适用于高速情况)。关于如何推导出有效达到最大速度所需的最大速度和时间的公式,我将解决每一个问题。我会放弃完整的推导,因为他们参与其中。


<强> Stokes' drag:

更新速度的公式为:

velocity += acceleration - friction*velocity

代表以下微分方程:

dv/dt = a - f*v

使用this integral table中的第一个条目,我们可以找到解决方案(假设t = 0时v = 0):

v = (a/f) - (a/f)*exp(-f*t)

当t>&gt;时出现最大(即终端)速度。 0,因此等式中的第二项非常接近零并且:

v_max = a/f

关于达到最大速度所需的时间,请注意方程式永远不会真正达到它,而是渐近方向。然而,当指数的参数等于-5时,速度大约是最大速度的98%,可能足够接近以认为它是相等的。然后,您可以将最大速度的时间近似为:

t_max = 5/f

然后,您可以使用这两个方程来求解 f a 给定所需的 vmax tmax


<强> Quadratic drag:

更新速度的公式为:

velocity += acceleration - friction*velocity*velocity

代表以下微分方程:

dv/dt = a - f*v^2

使用this integral table中的第一个条目,我们可以找到解决方案(假设t = 0时v = 0):

v = sqrt(a/f)*(exp(2*sqrt(a*f)*t) - 1)/(exp(2*sqrt(a*f)*t) + 1)

当t>&gt;时出现最大(即终端)速度。为了使指数项远大于1,方程接近:

v_max = sqrt(a/f)

关于达到最大速度所需的时间,请注意方程式永远不会真正达到它,而是渐近方向。然而,当指数的参数等于5时,速度大约是最大速度的99%,可能足够接近以认为它是相等的。然后,您可以将最大速度的时间近似为:

t_max = 2.5/sqrt(a*f)

也相当于:

t_max = 2.5/(f*v_max)

对于所需的 vmax tmax tmax 的第二个等式将告诉您 f 应该是什么,然后你可以将其插入 vmax 的等式中,以获得 a 的值。


这看起来有点矫枉过正,但这些实际上是一些模拟拖拽的最简单方法!任何真的希望看到集成步骤的人都可以给我发电子邮件,我会将它们发送给您。在这里打字他们有点过分了。

另一点:我没有立即意识到这一点,但如果您使用我为 v(t)。如果你只是简单地从休息模拟加速度,并且你正在跟踪加速开始以来的时间,那么代码看起来就像:

position += velocity_function(timeSinceStart)

其中“velocity_function”是 v(t)的两个公式之一,您将不再需要速度变量。一般来说,这里有一个权衡:计算 v(t)可能比仅使用迭代方案更新速度(由于指数项)更加计算成本,但保证保持不变稳定而有界。在某些条件下(比如试图获得非常短的 tmax ),迭代会变得不稳定和爆炸,这是前向欧拉方法的常见问题。但是,保持对变量的限制(例如0 < f <1)应该可以防止这些不稳定。

此外,如果你感觉有点自虐,你可以整合 v(t)的公式来获得 p(t)的封闭式解决方案,因此完全需要牛顿迭代。我会把这个留给别人去尝试。 =)

答案 1 :(得分:3)

警告:部分解决方案

如果我们遵循所述的物理学,则没有最大速度。从纯物理角度来看,你已经将加速度固定在一个恒定值,这意味着速度总是在增加。

作为替代方案,请考虑作用于您物体的两种力量:

  • 持续的外力, F ,往往加速它,
  • 阻力 d ,与速度成正比,往往会减慢速度。

因此,迭代n的速度变为: v n = v 0 + n F - d v n-1

您已经要求选择迭代nmax时出现的最大速度 v nmax

请注意,问题是欠约束;也就是说, F d 是相关的,因此您可以随意选择其中一个值,然后计算另一个。

既然球在滚动,有人愿意拿起数学吗?

警告:这很丑陋并涉及power series


编辑:为什么第一个等式中的序列n**F**按字面显示,除非在n之后有空格?

答案 2 :(得分:2)

velocity *= friction;

这并不能阻止速度在某一点上移动......

随着速度的增加,摩擦力呈指数增长(不要引用我的话),静止时它会为0。最终,你将达到摩擦=加速的程度。

所以你想要这样的东西:

velocity += (acceleration - friction);
position += velocity;
friction = a*exp(b*velocity);

您为a和b选择值的位置。 b将控制达到最高速度所需的时间,并且将控制摩擦力的增加。 (再一次,不要对此进行自己的研究 - 我将从12年级物理学的记忆中走出来。)

答案 3 :(得分:2)

这不是回答你的问题,但在这样的模拟中你不应该做的一件事取决于固定的帧速率。计算自上次更新以来的时间,并在方程中使用delta-T。类似的东西:

static double lastUpdate=0;
if (lastUpdate!=0) {
  deltaT = time() - lastUpdate;
  velocity += acceleration * deltaT;
  position += velocity * deltaT;
}
lastUpdate = time();

检查你是否失去焦点并停止更新也很好,当你获得焦点时将lastUpdate设置为0.这样你回来时就不会得到一个巨大的deltaT来处理。

答案 4 :(得分:1)

如果您想了解使用非常简单数学的非常简单物理模型可以做些什么,请查看{{3}处的一些Scratch项目} - 你可能会得到一些有用的想法和你肯定会玩得很开心。

答案 5 :(得分:1)

这可能不是您正在寻找的东西,但根据您正在使用的引擎,使用由其他人构建的引擎可能会更好,例如farseer(对于C#)。 注意 Codeplex已关闭进行维护。