编程从当前速度矢量到目标矢量的推力的平滑变化

时间:2009-09-02 01:05:12

标签: math vector physics linear-algebra angle

TL;博士:“我不知道如何计算一个矢量与另一个矢量之间的推力的平滑过渡。”

我正在编写一个简单的游戏,敌人在开放空间(没有墙壁)追逐玩家。我在计算敌人的x& y速度独立,如果他们将它们朝向玩家的方向加速它们,并且如果他们走错了方向则迅速减速(例如EnemyVelocity.x> 0& player.x< enemy.x,然后是EnemyVelocity。 x - 2。)

虽然游戏玩法试图躲避敌人是非常有趣的,但我希望让敌人使用适当的物理行为。我目前正在做的是让敌人根据他们和玩家之间的角度设置他们的推力(想象一艘宇宙飞船),让他们的推力加速到最大速度(计算EnemyVelocity三角形的c侧)。一旦发生这种情况,我不确定推力调整的最佳方式。如果我没有留下最大速度,敌人会很好地加速,但很容易超过玩家,然后需要很长时间才能获得足够的动力回到玩家的方向。

我想要发生的事情是让敌人在前往玩家的路上不断调整速度,瞄准他们所处的位置(我不希望他们预测你将在哪里)。然后,当他们错过了球员时,我想要同样的推力&加速公式,以重新调整他们的速度,并将其发送回玩家。

我认为这将涉及两个向量:一个是敌人目前正在旅行的地方,另一个是敌人想要旅行的地方(将他们直接带到玩家的向量)。我不确定如何计算一个矢量与另一个矢量之间的推力的平滑过渡。

任何提示,公式或问题都将非常感谢!谢谢Stack Overflow。

7 个答案:

答案 0 :(得分:2)

您可以通过确保速度的平滑变化而不是推力来获得您想要的效果。这样,如果敌人超过了玩家,它可以立即反转其加速度,从而减慢速度并最终改变其行进方向。

你可以通过在每次迭代期间改变速度来实现这个目标,这个数量基于从敌人到玩家的距离:

while (game_in_progress)
{
    // Distance from enemy to player.  The larger the
    // distance, the greater the acceleration will be.
    delta.x = player.x - enemy.x
    delta.y = player.y - enemy.y

    // Accelerate by changing velocity based on distance,
    // where 'scale' is sufficiently small. (Limit v to
    // some maximum if you choose; likely to be unnecessary.)
    v.x += delta.x * scale
    v.y += delta.y * scale

    // Update the enemy's position.
    enemy.x += v.x
    enemy.y += v.y
}

通过独立计算xy值,您可以省去处理矢量,角度和同时平衡的麻烦。

同样,通过认识到加速度(推力)只是速度的变化,而这又是位置的变化,你可以用简单的代数而不是微积分来创建离散时间模拟。

玩得开心!

答案 1 :(得分:2)

你需要用适当的物理术语思考。你有一个速度,你想添加一个加速度。这就是它的全部 - 加速度是速度的逐渐变化,它将吸引敌人朝向玩家,允许它超调,减速(或转向)然后返回玩家。

加速度以d(速度)/时间测量。你想在任何时间点向玩家加速,所以每个间隔(秒,百分之一秒或你选择的任何东西)你需要在敌人和玩家之间加上一个常数乘以你的速度。

Velocity = Velocity + c * (Player-Enemy vector)

常数c取决于您想要加速播放器的速度,以及更新速度的频率。

如果你想要“限制”敌人的最大速度,使其不会无限期地继续增加其速度的大小,你也可以这样做。

Velocity = Velocity * (Maximum magniture / |Velocity|)

编辑:为了进一步澄清,添加Velocity只是意味着添加组件向量。所以

Vx = Vx + c * Ax
Vy = Vy + c * Ay

其中V是速度,A是加速度。幅度测量为sqrt(Vx^2 + Vy^2),即直角三角形的斜边。所以,如果你想让敌人的最大速度为m,

Vx = Vx * ( m / sqrt(Vx^2 + Vy^2)
Vy = Vy * ( m / sqrt(Vx^2 + Vy^2)

答案 2 :(得分:2)

这一切都回到了牛顿方程式:

F = m * a
s = s_o + v * t + a * t^2 / 2
v = v_o + a * t

在这种情况下F是力(推力),a是加速度,m是船的质量。 s是当前位置,s_o是原始位置,v是速度,t是当前时间。

当然这是沿着一条直线,所以如果你想转换成二维或三维,你将不得不做一些数学运算。 Fsva都是向量,这意味着他们的方向同样重要。技术上t也是一个向量,但由于时间通常只朝一个方向发展,我们不必担心。

2d:
F^2 = F_x^2 + F_y^2 (use Pythagorean theorem to split force into components)
F_x = m * a_x
F_y = m * a_y
s_x = s_o_x + v_x * t + a_x * t^2 / 2
s_y = s_o_y + v_y * t + a_y * t^2 / 2
v_x = v_o_x + a_x * t
v_y = v_o_y + a_y * t

3d:
F^2 = F_x^2 + F_y^2 + F_z^2 (surprisingly, this works)
F_x = m * a_x
F_y = m * a_y
F_z = m * a_z
s_x = s_o_x + v_x * t + a_x * t^2 / 2
s_y = s_o_y + v_y * t + a_y * t^2 / 2
s_z = s_o_z + v_z * t + a_z * t^2 / 2
v_x = v_o_x + a_x * t
v_y = v_o_y + a_y * t
v_z = v_o_z + a_z * t

现在要根据玩家的方向调整你的速度,你有一个固定的总力(F),以便改变你当前的速度向玩家。在物理学中,事情不会立即发生,但你的目标应该是尽量减少变化发生的时间('t')。

根据您当前的位置((s_o_x,s_o_y)(s_o_x,s_o_y,s_o_z))和对手的当前位置或目标位置((s_x,s_y)(s_x,s_y,s_z)),您可以得到一个等式,对于你的目标速度(忽略加速度)。

v_x = (s_x - s_o_x) / t
v_y = (s_y - s_o_y) / t

v_x = (s_x - s_o_x) / t
v_y = (s_y - s_o_y) / t
v_z = (s_z - z_o_y) / t

我们可以将其替换为我们的其他等式:

(s_x - s_o_x) / t = v_o_x + a_x * t
(s_y - s_o_y) / t = v_o_y + a_y * t

(s_x - s_o_x) / t = v_o_x + a_x * t
(s_y - s_o_y) / t = v_o_y + a_y * t
(s_z - z_o_y) / t = v_o_z + a_z * t

然后我们求解加速度(这与力,这是我们试图计算的)。

(s_x - s_o_x) / t^2 - v_o_x / t = a_x
(s_y - s_o_y) / t^2 - v_o_y / t = a_y

(s_x - s_o_x) / t^2 - v_o_x / t = a_x
(s_y - s_o_y) / t^2 - v_o_y / t = a_y
(s_z - z_o_y) / t^2 - v_o_z / t = a_z

将其插入力方程:

F_x = m * (s_x - s_o_x) / t^2 - m * v_o_x / t
F_y = m * (s_y - s_o_y) / t^2 - m * v_o_y / t

F_x = m * (s_x - s_o_x) / t^2 - m * v_o_x / t
F_y = m * (s_y - s_o_y) / t^2 - m * v_o_y / t
F_z = m * (s_z - z_o_y) / t^2 - m * v_o_z / t

现在解决t

t = (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
t = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y

t = (-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
t = (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
t = (-m * v_o_z +/- sqrt(m^2 * v_o_z^2 - 4 * F_z * m * (s_z - s_o_z))) / 2 / F_z

时代应该趋同,所以时间会平等!这为我们提供了每个坐标(平面和球体)的方程组。请注意,有多个可能的值,但有些值涉及虚数,因此您必须消除这些解决方案:

(-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
= (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
F^2 = F_x^2 + F_y^2

(-m * v_o_x +/- sqrt(m^2 * v_o_x^2 - 4 * F_x * m * (s_x - s_o_x))) / 2 / F_x
= (-m * v_o_y +/- sqrt(m^2 * v_o_y^2 - 4 * F_y * m * (s_y - s_o_y))) / 2 / F_y
= (-m * v_o_z +/- sqrt(m^2 * v_o_z^2 - 4 * F_z * m * (s_z - s_o_z))) / 2 / F_z
F^2 = F_x^2 + F_y^2 + F_z^2

解决(F_x,F_y)(F_x,F_y,F_z)坐标,你就可以获得所需的力量。

如果您有任何问题,或者您在我的数学中发现错误,请告诉我。

答案 3 :(得分:1)

我曾经写过一个简单的小行星游戏,它有一个“盟友”号船,可以追捕小行星并向你射击。基本上它发现了最近的小行星然后开始顺利地转向并继续它。可悲的是我不再有代码了,但是如果记忆服务,我想我每次转弯都有一点船,然后如果小行星很远,我加速但是如果它接近我试图匹配小行星的速度。实际上它非常酷,并且涉及最少的代数。

最好的方法是在它们之间采用2弧度值和lerp,处理包装。 (可能在必要时加上或减去2pi)。然后将其转换为单位矢量。随后乘以你希望船速加速的速度,然后你就去了!

答案 4 :(得分:1)

一种简单的方法(不适当的物理学)是计算你的敌人的“所需速度”,然后调整敌人当前的速度朝向它,注意顶部的任何限制或最低速度。

例如,在我写的(http://wordwarvi.sourceforge.net)的一个小小的游戏中,有“寻热导弹”。如果导弹停在半空中转身,看起来很奇怪。所以我做的如下:我计算出一个朝向玩家的“所需速度”。这只是由“类似的三角形”完成的。我找到了X中的玩家距离,并且在Y中,哪个更大,我将“所需(x或y)速度设为最大可能速度,然后缩放另一个速度以适合”相似的三角形“。 ,这只是“所需的速度”,而不是当前的速度。我采用当前速度并缓慢调整(每帧一点点)朝向“所需”的速度(尽管每帧也重新计算所需的速度)记住vx和vy上的最小值,以防止他们停在半空中。

哑算法,但它运作正常(没有人抱怨它们太容易,太难或太不现实。)

编辑:在重新阅读问题时,我的回答可能不是你想要的。

答案 5 :(得分:1)

我已经专业地解决了这样的问题,我建议你从简单的版本开始,然后开始工作。在尝试下一步之前,请确保在每一步都获得预期的行为。

  1. 寻求者在一个维度寻找原点的静止目标。这是正确的,一个方面。它可以在x轴上来回推动,它试图达到x = 0。推进器没有油门(如固体火箭),但导引头可以指向任一方向。如果您正确编程,导引头将在x = 0附近振荡,每次都会超调。
  2. 同样,但目标在x = 0以外的某个地方是静止的。只做x相对而不是绝对(也就是说,寻求者关心x中的差异,而不是目标的x)。
  3. 现在目标正在移动(或跳跃)。寻求者应该能够跟随它。根据目标的移动方式,振荡会增大或缩小 - 你会明白我的意思。
  4. 现在有两个维度。导引头总是直接朝向目标,这意味着你必须通过简单的三角形将推力分成x和y分量。如果移动目标,导引头可能会进入轨道。
  5. 回到一个维度和一个固定的目标,但现在寻求者正在尝试一个集合,而不是一个飞越。这是困难的部分。目标是使距离和速度同时变为零,没有过冲,因此导引头必须知道自己的制动能力。当x小于v ^ 2 / 2a时,导引头必须反向推力,将推离目标以便减速并与之相遇。让导引头在非常接近目标时停止推进是很好的。
  6. 目标再次开始移动。这很简单;只是使x和v相对,而不是绝对。
  7. 多个维度。这非常容易; x,y和z部分是独立的。

现在如果你想要一个不能打开硬币,但必须曲线的导引头,事情会变得棘手......

答案 6 :(得分:1)

只有一些指示可以让这一切变得简单明了。 1)使用向量而不是将所有内容写入两到三次是最简单也是最通用的。 2)如果你控制力(实际上是A = F /质量时的加速度),然后动态地演化速度和位置,事情会看起来正确。

逼真运动的基本循环看起来像(CAP是向量而dt是你的时间步长):

while (motion) {
   A = get_acceleration(X, V, A, X_target, V_targer, A_target)
   V += A*dt       // V is the integral of A
   X += V*dt       // X is the integral of V
}

实际上,这是关于你的动态进化。

然后您需要决定如何确定加速度,即写get_acceleration。这里有许多选择取决于多种因素,现实生活中的追逐者采用多种策略。例如,如果你相对于你的质量有很大的推力(即高加速度),你可能只想直接指向目标;但如果你相对于你的推力有很多质量,你可能想要制作一个拦截课程。如果你想在接近目标时减速,你可以在|X-X_target|变小(即它们接近)和/或它们的速度接近时反转加速度。此外,阻尼可以帮助不振荡的东西,为此,为-c*(V-V_target)之类的加速度添加一个术语。我建议你一起玩这些,直到你得到一些与你的目标相符的物理外观。