2D太空船运动数学

25

我正在尝试制作一款自上而下的宇宙飞船游戏,希望其运动方式有些逼真,包括360度惯性、重力等。

我的问题是,我可以轻松地使飞船以惯性360度移动,但我需要做的是在不限制其他推拉力的情况下,对引擎速度施加限制。

因此,如果发动机速度的最大值为500,而飞船从引力井中以1000的速度前进,那么当它的引擎启动时,飞船不会飞到1500,但如果指向的角度与它前进的角度相反,那么它可能会减速。

值得一提的是,我正在使用Construct,我只需要数学计算公式。

感谢任何帮助,我已经因尝试解决这个问题而秃顶了。

5个回答

25

借鉴相对论物理,其中物体不能超过光速的概念:

(下面是我工作中的C++代码片段和运行演示 [仅限Windows]。)

  1. 将常量c设置为物体可以达到的最大速度(也就是你游戏中的“光速”)。
  2. 如果施加力会增加物体的速度,则将加速度(速度变化)除以Lorentz因子。if条件在特殊相对论方面不太现实,但它使飞船在高速时更加“可控”。
  3. 更新:通常情况下,当速度接近c时,飞船很难操纵,因为改变方向需要加速度将速度推到c以上(Lorentz因子最终会将新方向上的加速度缩放到几乎为零)。要恢复操纵性,请使用速度矢量没有经过Lorentz缩放的方向,并使用缩放后速度矢量的大小。

解释:

Lorentz因子的定义,其中v是速度,c是光速: gamma = 1 / sqrt(1 - v^2 / c^2)

这是因为洛伦兹因子随着速度的增加趋近于无穷大。物体需要施加无限的力才能越过光速。在较低的速度下,洛伦兹因子非常接近1,近似于经典牛顿物理学。
速度增加时的洛伦兹因子图表:

alt text

注意:我曾尝试通过更改摩擦设置来解决我的小行星游戏中的类似问题。当我看到您的问题时,我刚想出这个解决方案^^

更新:我尝试实现此算法并发现一个潜在的缺陷:当接近光速c时,所有方向的加速度都会受到限制,包括减速!(与直觉相反,但在现实世界中的特殊相对论中是否会发生这种情况?)我猜可以修改该算法以考虑速度和力向量的方向...已经修改算法以考虑向量方向,使飞船在高速状态下不会失去可控性。

更新:这里有一段代码片段,来自我的小行星游戏,它使用洛伦兹因子来限制游戏对象的速度。它运行得非常好!

更新:*添加了可下载演示(仅适用于 Windows;其他平台请从源代码构建)。我不确定所有依赖项是否都包含在zip文件中,请告诉我是否有遗漏。祝玩得愉快^^

void CObject::applyForces()
{
    // acceleration: change in velocity due to force f on object with mass m
    vector2f dv = f/m;

    // new velocity if acceleration dv applied
    vector2f new_v = v + dv;

    // only apply Lorentz factor if acceleration increases speed
    if (new_v.length() > v.length())
    {
        // maximum speed objects may reach (the "speed of light")
        const float c = 4;

        float b = 1 - v.length_squared()/(c*c);
        if (b <= 0) b = DBL_MIN;

        double lorentz_factor = 1/sqrt(b);
        dv /= lorentz_factor;
    }

    // apply acceleration to object's velocity
    v += dv;

    // Update:
    // Allow acceleration in the forward direction to change the direction
    // of v by using the direction of new_v (without the Lorentz factor)
    // with the magnitude of v (that applies the Lorentz factor).
    if (v.length() > 0)
    {
        v = new_v.normalized() * v.length();
    }
}

2
很酷的技巧;我从未想过“修改光速”。 - dash-tom-bang
很棒的回答!(因为原创性而点赞!)但是(如果我错了请纠正我),在这个模型中,即使你绕过黑洞弹射或者被一艘拥有更大引擎(更高最大速度)的飞船牵引,你也不能超过你的最大速度。我认为OP可能只想要通过你自己的引擎实现有限的速度。 - Dan
@Dan:要实现“超光速”,只需在引擎力施加时应用洛伦兹因子即可。我将所有作用于物体上的力求和,找到净加速度,但每个力都可以单独施加,每个力的c值也可能不同!(我认为这可能会导致奇怪的行为,具体取决于施加力的顺序...) - Leftium
在这种情况下,如果其他力量将飞船加速到比引擎的“光速”更快,那么引擎的洛伦兹因子将变为未定义。当然,您可以通过条件语句解决这个问题,但可能仍然很奇怪。(实际上,可以证明在真正的特殊相对论中,不能存在多于一个极限速度。) - David Z
抱歉我的无知,那么"/="是什么意思? - YAS
显示剩余3条评论

2

我很喜欢Wongsungi的答案(使用洛伦兹因子),但是我想指出代码可以简化为更少的浮点运算。

与其计算洛伦兹因子(本身就是倒数),然后再除以它,像这样:

        double lorentz_factor = 1/sqrt(b);
        dv /= lorentz_factor;

只需将其乘以洛伦兹因子的倒数,如下所示:

        double reciprocal_lorentz_factor = sqrt(b);
        dv *= reciprocal_lorentz_factor;

这将从代码中省去一个浮点运算,并且消除了将b夹紧到 DBL_MIN 的需要(现在它可以被夹紧到0,因为我们不再进行除法)。既然可以通过乘以x来代替x的倒数进行除法运算,那么为什么还要使用除法呢?
此外,如果你能保证v的大小永远不会超过c,那么你可以消除对b小于零的测试。
最后,在外部的if语句中使用length_squared()代替length(),可以消除另外两个sqrt()操作。
    if (new_v.length_squared() > v.length_squared())
    {
        const float c = 4;

        float b = 1 - v.length_squared()/(c*c);
        if (b < 0) b = 0;

        double reciprocal_lorentz_factor = sqrt(b);
        dv *= reciprocal_lorentz_factor;
    }

这样做可能只会使速度提高0.1%,但我认为以这种方式编写代码更简单。


2
首先,让我们考虑现实问题,看看为什么这种方法行不通,以及我们该如何与之区别。在太空中,只要你的引擎在运转,你就会加速。你的速度只受燃料限制(事实上,一旦你消耗了一些燃料,你可以加速得更快,因为你移动的质量更少)。
为了给这个模型一个有效的最大速度,你可以考虑太空中的粒子会减速并产生摩擦力。你越快,撞到的粒子就越多,而且你撞击它们的速度也越快,所以最终在足够快的速度下,你会撞击足够多的粒子,使得它们产生的减速恰好抵消了引擎产生的加速度。
这个现实模型听起来并不像你想要的。原因是:你必须引入摩擦力。这意味着如果你切断引擎,你就会自动开始减速。你可能会认为这是你不想要的意外力量之一。
这就使我们只能将引擎的有效推力在达到一定速度后降为0。现在请记住,如果你以北方向的最高速度行驶,你仍然希望引擎能够推动你向东方向移动,因此你的引擎不应该仅仅因为速度过快而被切断,而应该基于你在引擎所指方向上的速度。
因此,数学上的做法是:对于引擎指向向量和速度向量进行点积以获得引擎指向方向的有效速度。一旦你获得了这个速度,比如说125英里/小时(最高速度为150),那么你就可以将引擎产生的推力缩减为(150-125)/150 *(引擎推力)。
这将大大改变速度图表,使你从加速到全速所需的时间发生变化。随着你接近最大速度,你的引擎变得越来越无力。测试一下,看看它是否符合你的要求。另一种方法是,如果点积>=150,则将引擎推力设为0,否则为满推力。这将允许你线性加速到最大速度,但不会超过这个速度。
现在我想起来,这个模型并不完美,因为你可以以北方向的150英里/小时的速度加速,然后转向东方并加速到150英里/小时,总共达到212英里/小时的东北方向,因此不是一个完美的解决方案。

谢谢,我尝试了这个方法,但是当角度改变时(为简单起见,假设角度改变了180度),引擎从未扩大到全力。因此,如果速度为150,角度为0,则引擎力为0,但当船掉头时,引擎力仍然为0,但需要回到150。所以我需要一种计算船在某个角度下速度的方法,我猜?如果我表达不清楚,还请见谅,并感谢您的帮助! - YAS
摩擦力可能是处理这个问题最容易的方法。使它们与速度大小成比例,并调节它们的强度,以限制飞船可以达到的速度。不要担心现实主义;你已经说过你不追求它,并且(非常脆弱的)太空中有气体可以提供摩擦力。当然,如果这是一个真正逼真的模型,那么你将不得不考虑到气体流动的事实,以及在恒星系统内它实际上是一种快速移动的等离子体(太阳风)。 :-) - Donal Fellows
什么粒子,在太空中有什么摩擦力?好吧,除非他飞进星云里:)。实际上,这是来自物理学的另一部分。太空中的密度可以忽略不计。 - Andrey
@David 谢谢... 我以前从来没有划掉过“cross”这个词。看起来很合适。 - Dan
@Andrey,即使是微小的摩擦力在足够高的速度下也会变得显著。虽然在这种情况下可能需要达到相对论速度才会有影响,但我认为星际旅行仍会有相当数量的粒子存在。另一方面,在星系之间的真空中,粒子数量确实非常少。但是,如果您想要合理的速度限制,您将不得不使用比实际值高得多的摩擦因素。 - Dan
显示剩余2条评论

1

你需要为你的飞船设置三个变量,在每个物理时间步长中根据作用于它的力进行更新。这些变量将是质量、位置和速度。(请注意,位置和速度是单个数字但是向量)。在每个物理时间步长中,您根据加速度更新位置,并根据力更新速度。您可以根据作用于飞船上的力(重力、摩擦、引擎)来计算加速度。

牛顿关于力的方程式是 F = M*A 我们可以重新排列得到 A = F/M 来获得加速度。基本上,你需要弄清楚飞船应该加速多少,以及加速的方向(向量),然后将该加速度添加到飞船的速度中,并将飞船的速度添加到其位置中。

以下是您应该在每个物理时间步骤中执行的代码(我希望您能填写空白处),如果这不够详细,请随时询问。

gravity = //calculate force of gravity acting on ship from Newton's law of universal gravitation
friction = //ten percent of the ship's velocity vector, in the opposite direction
engines = 0
if (engines_are_firing)
    engines = 500
forces = gravity + friction + engines
acceleration = forces / ship.mass
ship.velocity += acceleration
ship.position += velocity
redraw()

我不了解你正在使用的游戏创建器,但如果它没有内置的物理引擎,那么它作为一个游戏创建器就很糟糕。也许它有一个,你可以去了解一下。 - Nathan
构建实现了Newton Game Dynamics(http://newtondynamics.com/forum/newton.php)。我认为@YAS可能正在使用它,因为他/她只需要数学部分(你正确地提供了)。 - ABach
1
这只是牛顿方程的一个基本实现;它不会像YAS所要求的那样以任何方式限制速度。 - Thomas
1
@Thomas 如果你假设引擎以最大力量喷射,并假设船的重量恒定且存在一些摩擦系数,那么你最终会在某个最大速度处停止加速(在这个模型中,你不能仅凭引擎的动力超过它...但可以通过黑洞或其他物体进行弹弓操作,这正是OP想要的)。虽然“最大速度”不是输入之一,但如果知道船只的引擎功率,可以通过改变所使用的摩擦系数来使最大速度达到任何你想要的值。 - Dan

0

我很难理解你的问题,但似乎你没有在游戏中使用真实的物理学。你考虑过使用真实的物理方程式,如速度、加速度、力等吗?

编辑: 经过您的编辑,我认为我有了更好的理解。您只是跟踪当前速度(或类似的东西),但您没有跟踪产生该速度的力量。飞船不应存储任何这些信息(除了引擎推力)——它应该来自飞船所处的环境。

例如,环境具有重力向量(方向力),因此在计算引擎提供的方向力时,您需要考虑到这一点。

您的飞船应存储其自身的引擎力、加速度和速度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接