从当前速度向量平稳过渡到目标向量的推力编程

7
TL;dr: "我不确定如何计算在一个向量和另一个向量之间实现平滑推力转换。"
我正在编写一个简单的游戏,其中敌人在开放空间中(没有墙壁)追逐玩家。我独立地计算敌人的x和y速度,并在它们朝向玩家的方向上加速它们,如果它们朝错误的方向前进,则快速减速它们(例如,EnemyVelocity.x> 0且player.x < enemy.x,则EnemyVelocity.x - 2)。
虽然躲避敌人的游戏玩法还不错,但我的愿望是让敌人使用适当的物理行为。我目前所做的是根据它们与玩家之间的角度设置敌人的推力(类似于宇宙飞船),并有他们的推力加速到最大速度(计算EnemyVelocity三角形的c边)。一旦发生这种情况,我不确定最好的方式是让推力自动调整。如果我不设置最大速度,敌人会很好地加速,但很容易超过玩家,然后需要很长时间才能重新获得足够的动量朝向玩家的方向。
我想要发生的是,敌人在前往玩家的路上不断调整他们的速度,瞄准他们所在的位置(我不希望他们预测您将在何处)。然后,当他们错过玩家时,我希望同样的推力和加速公式重新调整他们的速度,并将它们送回玩家。
我认为这将涉及两个向量:一个是敌人当前行进的向量,另一个是敌人想要行进的向量(将它们直接带到玩家)。我不确定如何计算在一个向量和另一个向量之间实现平滑推力转换。
非常感谢任何提示、公式或问题!谢谢Stack Overflow。

1
有关拦截导弹游戏中 AI 方面的讨论,请参见以下链接:https://dev59.com/eXM_5IYBdhLWcg3w2XBg - dmckee --- ex-moderator kitten
如果敌人只攻击玩家所在的位置,除非他/她站着不动,否则他们永远也抓不住玩家... - Steven A. Lowe
@Steven:……除非玩家以比敌人更慢的速度沿着恒定的方向移动。再说,旧恐怖电影中的怪物总是以蜗牛的速度追逐青少年,但这并不会使它们变得不那么可怕。;-) - Adam Liss
有其他的游戏因素会让玩家被敌人抓住,即玩家避开其他物品。感谢大家的回复! - user128526
7个回答

2
您需要以正确的物理术语进行思考。您有一个速度,想要增加一个加速度。这就是所有的内容 - 加速度是速度逐渐变化,会将敌人拉向玩家,使其超过,减速(或转向),然后向玩家返回。
加速度被测量为d(速度)/时间。您希望在任何时间点向玩家加速,因此每个间隔(秒,百分之一秒或您选择的任何时间)需要将敌人和玩家之间的向量乘以某个常数添加到您的速度中。
Velocity = Velocity + c * (Player-Enemy vector)

常数c取决于您希望向玩家加速的速度以及更新速度的频率。

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

Velocity = Velocity * (Maximum magniture / |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)

然后显然将新速度“添加”到敌人的位置。 :-) - Kirk Broadhurst
我不确定我理解以下公式的数学含义: Velocity = Velocity * (Maximum magniture / |Velocity|). 在 Vx = Vx + c * Ax 中,Ax 应该是玩家和敌人之间向量的 X 分量,对吗? - user128526
是的,在这个练习中,Ax是玩家-敌人向量的x分量。更一般地说,Ax是加速度的x分量,但使用玩家-敌人向量作为加速度就足够了。 - Kirk Broadhurst
Kirk的本质与Imagist相同,只是Kirk封装了向量数学(隐藏),而Imagist则明确表示。这就是为什么Kirk看起来很简单,但你无法弄清楚数学原理,而Imagist看起来太复杂,但数学原理是直截了当的原因。 - RBarryYoung
谢谢RBarry - 这是真的,但我认为当我将其分解为x和y分量时,我明确说明了所需的计算。我只是没有展示所有的工作过程! - Kirk Broadhurst

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 值,您可以避免处理向量、角度和同时方程所带来的麻烦。
同样地,通过认识到加速度(推力)只是速度变化,而速度又是位置变化,您可以使用简单的代数而不是微积分创建离散时间模拟。
祝您玩得开心!

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) 坐标,即可得到所需的力量。

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


谢谢您的帖子。看起来这是最复杂的,所以在实施此方法之前,我会尝试其他几种方法! - user128526
1
如果您创建一些向量类(2D或3D)来封装2D或3D算术,那么数学和代码就会变得简单得多。 - RBarryYoung
1
RBarryYoung是正确的,使用向量类会更加简洁。此外,虽然这看起来非常复杂,但这也是“现实世界的做法”。习惯使用牛顿方程将使您所做的所有运动算法更容易。 - Imagist
嗯,这是一个很好的近似值,符合“现实世界的做法”,并经过了爱因斯坦提供替代方案之前数百年的考验 :-) - Will
下划线过载。我的眼睛。 - ine

1
我之前写过一个简单的小行星游戏,其中有一艘“盟友”飞船,可以追踪小行星并为你射击。基本上它会找到最近的小行星,然后平滑地转向并追击它。可惜我现在没有代码了,但如果我没记错的话,我每次都会稍微转动一下飞船,然后如果小行星离得很远,我就加速,但如果它靠近了,我就试图匹配小行星的速度。实际上这很酷,而且只需要最少的代数知识。
最好的方法是取两个弧度值并在它们之间进行线性插值,处理包装(如果必要,通过添加或减去2pi)。然后将其转换为单位向量。随后将其乘以您希望飞船加速的速度,就完成了!

1

一个简单的方法(不是很正式的物理学方法)是计算你的敌人的“期望速度”,然后调整敌人当前的速度朝向那个方向,注意速度上限或最小速度。

例如,在我写的一个小2D游戏中(http://wordwarvi.sourceforge.net),有“热导弹”。如果导弹在半空中停下来掉头看起来非常奇怪。所以我做的是:计算一个“期望速度”,它指向玩家。这只是通过“相似三角形”完成的。我找到了X和Y轴上到玩家的距离,哪个更大,我就让“期望(x或y)速度”尽可能大,然后按比例缩放另一个速度以适应“相似三角形”。请注意,这只是“期望速度”,而不是当前速度。我将当前速度慢慢地(每帧少量)调整到“期望”速度(尽管期望速度也会每帧重新计算),同时注意vx和vy的最小值,以防止它们在半空中停止。

这是一个愚蠢的算法,但它运行得还不错(没有人抱怨它们太容易、太难或太不现实)。

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


你如何计算调整?例如,如果目标速度比“X”偏离了更多的“Y”,那么您会减去X并加到Y上吗? 当您说“所需的x或y”时,您是指基于哪个对玩家更重要,对吗?(只是确认我认为您说的话)。 - user128526
1
为什么这不是“合适的物理学”?你所描述的几乎就是通过改变推进器方向来控制宇宙飞船航向的情况。如果速度调整有所变化,那么你也在控制推进器的功率。如果它是恒定的,那么你的推进器就会一直保持全功率。我猜这是NASA实际考虑到的情况。 :-) - Adam Liss
Adam Liss:这不是恰当的物理学,因为我没有做F = ma之类的事情,例如,我的最大速度在x和y方向上独立限制,因此该物体可以在对角线方向上行驶sqrt(2)* max,但只能在水平或垂直方向上行驶最大值。这是一个非常粗略的近似,仅足够在视频游戏中玩乐。 - smcameron
cmpstudio:查找“相似三角形”。如果X和Y是到目标的距离,则通过相似三角形X/Y == dvx/dvy,其中dvx,dvy是所需的x和y速度。无论X或Y哪个具有最大的幅度,都要将相应的dvx或dvy最大化。那只剩下一个未知数了。解决X/Y == dvx/dvy的未知数。然后,每帧将对象的当前速度调整为计算出的dvx,dvy。这里有一些示例代码: http://wordwarvi.cvs.sourceforge.net/viewvc/wordwarvi/wordwarvi/wordwarvi.c?revision=1.400&view=markup请查看围绕第6300行的函数missile_move。 - smcameron

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部分是独立的。
现在,如果您想要一个不能立即转向而必须曲线行驶的搜索器,那么情况就变得棘手了...

1

只需要遵循几个指针就可以轻松正确地完成这项工作。1)最容易且最常见的方法是使用向量而不是重复两次或三次写入所有内容。2)如果您控制力(实际上是加速度,因为A = F /质量),然后动态演变速度和位置,事物看起来就会正确。

您用于实现逼真运动的基本循环如下(其中CAPs是向量,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)。 我建议您尝试这些方法,直到找到与您的物理外观和感觉相匹配的东西。


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