我正在开发一个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;
}这是一种非常简单的方法,它不依赖于质量或实际的摩擦力(代码中的摩擦力只是作用于运动的一般力)。它起到了"velocity *= friction;“部分的作用,可以防止速度超过某一点。然而,正是这种最高速度和它与加速度和摩擦力的关系让我有点迷失。
我想要做的是设置一个最高速度,以及达到它所需的时间,然后用它们来推导加速度和摩擦力值。
即,
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 = ?发布于 2009-03-20 17:13:38
我发现这个问题非常有趣,因为我最近做了一些关于带阻力的抛射运动建模的工作。
点1:您实际上是在使用explicit/forward Euler iteration更新位置和速度,其中状态的每个新值都应该是旧值的函数。在这种情况下,您应该先更新位置,然后再更新速度。
要点2: the effect of drag friction有更真实的物理模型。一种模型(由Adam Liss建议)涉及与速度成比例的阻力(称为斯托克斯阻力,通常适用于低速情况)。我之前建议的阻力与速度的平方成正比(称为平方阻力,通常适用于高速度情况)。我将讨论如何推导出最大速度的公式,以及有效地达到最大速度所需的时间。我将忽略完整的派生,因为它们相当复杂。
更新速度的方程式为:
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 >> 0时,因此方程中的第二项非常接近于零,并且:
v_max = a/f关于达到最大速度所需的时间,请注意,方程永远不会真正达到它,而是逐渐接近它。然而,当指数的自变量等于-5时,速度大约是最大速度的98%,可能足够接近到认为它相等。然后,您可以将达到最大速度的时间近似为:
t_max = 5/f然后,您可以使用这两个方程来求解给定所需vmax和tmax的f和a。
更新速度的方程式为:
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 >> 0时,因此指数项远大于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)推导的公式,速度的更新就不再是必要的了。如果您简单地从rest建模加速,并且跟踪自加速开始以来的时间,则代码将如下所示:
position += velocity_function(timeSinceStart)其中"velocity_function“是v(t)的两个公式之一,您将不再需要速度变量。通常,这里有一个权衡:计算v(t)可能比简单地使用迭代方案更新速度(由于指数项)的计算成本更高,但它可以保证保持稳定和有界。在某些情况下(比如试图得到一个非常短的tmax),迭代可能会变得不稳定和崩溃,这是前向欧拉方法的常见问题。然而,保持对变量的限制(如0< f < 1),应该可以防止这些不稳定性。
此外,如果您觉得有点受虐待,您可以集成v(t)的公式,以获得p(t)的封闭形式解,从而完全省去了牛顿迭代的需要。我将把这个留给其他人去尝试。=)
发布于 2009-03-20 17:11:50
velocity *= friction;这并不能阻止速度在某一点左右移动。
摩擦力随着速度的增加呈指数级增加(不要引用我的话),在静止状态下将为0。最终,你会达到一个摩擦力=加速度的点。
所以你想要这样的东西:
velocity += (acceleration - friction);
position += velocity;
friction = a*exp(b*velocity);b将控制达到最高速度所需的时间,而a将控制摩擦力增加的突然程度。(再说一次,不要做你自己的研究-我是从我记得的12年级物理开始的。)
发布于 2009-03-20 21:09:35
这不是在回答你的问题,但是有一件事你不应该在这样的模拟中做,那就是依赖于固定的帧速率。计算自上次更新以来的时间,并在方程式中使用增量-T。类似于:
static double lastUpdate=0;
if (lastUpdate!=0) {
deltaT = time() - lastUpdate;
velocity += acceleration * deltaT;
position += velocity * deltaT;
}
lastUpdate = time();如果你失去焦点并停止更新,以及当你获得焦点时将lastUpdate设置为0,这也是很好的检查。这样一来,当您返回时,就不会有大量的deltaT需要处理。
https://stackoverflow.com/questions/667034
复制相似问题