我正在研究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 = ?
答案 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)
如果我们遵循所述的物理学,则没有最大速度。从纯物理角度来看,你已经将加速度固定在一个恒定值,这意味着速度总是在增加。
作为替代方案,请考虑作用于您物体的两种力量:
因此,迭代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已关闭进行维护。