首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >简单的基于物理的移动

简单的基于物理的移动
EN

Stack Overflow用户
提问于 2009-03-20 16:57:37
回答 5查看 26.7K关注 0票数 31

我正在开发一个2D游戏,我试图使用一些基本的物理代码将物体加速到最高速度。

下面是它的伪代码:

代码语言:javascript
复制
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;“部分的作用,可以防止速度超过某一点。然而,正是这种最高速度和它与加速度和摩擦力的关系让我有点迷失。

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

即,

代码语言:javascript
复制
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     = ?
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2009-03-20 17:13:38

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

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

要点2: the effect of drag friction有更真实的物理模型。一种模型(由Adam Liss建议)涉及与速度成比例的阻力(称为斯托克斯阻力,通常适用于低速情况)。我之前建议的阻力与速度的平方成正比(称为平方阻力,通常适用于高速度情况)。我将讨论如何推导出最大速度的公式,以及有效地达到最大速度所需的时间。我将忽略完整的派生,因为它们相当复杂。

更新速度的方程式为:

代码语言:javascript
复制
velocity += acceleration - friction*velocity

它代表下面的微分方程:

代码语言:javascript
复制
dv/dt = a - f*v

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

代码语言:javascript
复制
v = (a/f) - (a/f)*exp(-f*t)

最大(即终端)速度出现在t >> 0时,因此方程中的第二项非常接近于零,并且:

代码语言:javascript
复制
v_max = a/f

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

代码语言:javascript
复制
t_max = 5/f

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

更新速度的方程式为:

代码语言:javascript
复制
velocity += acceleration - friction*velocity*velocity

它代表下面的微分方程:

代码语言:javascript
复制
dv/dt = a - f*v^2

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

代码语言:javascript
复制
v = sqrt(a/f)*(exp(2*sqrt(a*f)*t) - 1)/(exp(2*sqrt(a*f)*t) + 1)

最大(即终端)速度出现在t >> 0时,因此指数项远大于1,并且方程接近:

代码语言:javascript
复制
v_max = sqrt(a/f)

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

代码语言:javascript
复制
t_max = 2.5/sqrt(a*f)

这也等同于:

代码语言:javascript
复制
t_max = 2.5/(f*v_max)

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

这看起来有点夸张,但这些实际上是模拟拖动的一些最简单的方法!任何真正想看集成步骤的人都可以给我发一封电子邮件,我会把它们发给你的。它们有点太复杂了,不能在这里键入。

另一点:,我没有立即意识到这一点,但是如果你使用我为v(t)推导的公式,速度的更新就不再是必要的了。如果您简单地从rest建模加速,并且跟踪自加速开始以来的时间,则代码将如下所示:

代码语言:javascript
复制
position += velocity_function(timeSinceStart)

其中"velocity_function“是v(t)的两个公式之一,您将不再需要速度变量。通常,这里有一个权衡:计算v(t)可能比简单地使用迭代方案更新速度(由于指数项)的计算成本更高,但它可以保证保持稳定和有界。在某些情况下(比如试图得到一个非常短的tmax),迭代可能会变得不稳定和崩溃,这是前向欧拉方法的常见问题。然而,保持对变量的限制(如0< f < 1),应该可以防止这些不稳定性。

此外,如果您觉得有点受虐待,您可以集成v(t)的公式,以获得p(t)的封闭形式解,从而完全省去了牛顿迭代的需要。我将把这个留给其他人去尝试。=)

票数 40
EN

Stack Overflow用户

发布于 2009-03-20 17:11:50

代码语言:javascript
复制
velocity *= friction;

这并不能阻止速度在某一点左右移动。

摩擦力随着速度的增加呈指数级增加(不要引用我的话),在静止状态下将为0。最终,你会达到一个摩擦力=加速度的点。

所以你想要这样的东西:

代码语言:javascript
复制
velocity += (acceleration - friction);
position += velocity;
friction = a*exp(b*velocity);

b将控制达到最高速度所需的时间,而a将控制摩擦力增加的突然程度。(再说一次,不要做你自己的研究-我是从我记得的12年级物理开始的。)

票数 2
EN

Stack Overflow用户

发布于 2009-03-20 21:09:35

这不是在回答你的问题,但是有一件事你不应该在这样的模拟中做,那就是依赖于固定的帧速率。计算自上次更新以来的时间,并在方程式中使用增量-T。类似于:

代码语言:javascript
复制
static double lastUpdate=0;
if (lastUpdate!=0) {
  deltaT = time() - lastUpdate;
  velocity += acceleration * deltaT;
  position += velocity * deltaT;
}
lastUpdate = time();

如果你失去焦点并停止更新,以及当你获得焦点时将lastUpdate设置为0,这也是很好的检查。这样一来,当您返回时,就不会有大量的deltaT需要处理。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/667034

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档