【问题标题】:Programming a smooth change of thrust from current velocity vector to a target vector对推力从当前速度矢量到目标矢量的平滑变化进行编程
【发布时间】:2009-09-02 01:05:11
【问题描述】:

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

我正在编写一个简单的游戏,其中一个敌人在空旷的空间(没有墙壁)中追逐玩家。我正在独立计算敌人的 x 和 y 速度,如果他们将他们带向玩家的方向,则加速他们,如果他们走错路,则迅速减慢他们(例如 EnemyVelocity.x > 0 & player.x

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

我希望让敌人在前往玩家的路上不断调整他们的速度,瞄准他们在哪里(我不希望他们预测你会在哪里)。然后,当他们错过玩家时,我希望使用相同的推力和加速度公式来重新调整他们的速度并将它们送回玩家。

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

任何提示、公式或问题将不胜感激!谢谢 Stack Overflow。

【问题讨论】:

  • 有关拦截 AI 方面的现有讨论,请参阅:stackoverflow.com/questions/1202391/…
  • 如果敌人只瞄准玩家所在的位置,除非玩家静止不动,否则他们永远无法抓住玩家……
  • @Steven: ...除非玩家沿恒定方向移动,比敌人慢。话又说回来,老恐怖电影中的怪物总是以蜗牛的速度追逐青少年,但这并没有让他们变得不那么可怕。 ;-)
  • 还有其他游戏因素会让玩家被敌人抓住,即玩家躲避其他事物。感谢大家的回复!

标签: math vector physics linear-algebra angle


【解决方案1】:

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

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) 来改变你当前对玩家的速度。在物理学中,事情不会立即发生,但您的目标应该是尽量缩短变化发生的时间(“不”)。

这会根据您的当前位置((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) 坐标,您就获得了所需的力。

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

【讨论】:

  • 感谢您的帖子。它看起来是最复杂的,所以我将在实现之前尝试其他几个!
  • 如果你制作一些向量类(2d 或 3d)来封装 2d 或 3d 算术,数学和代码会变得简单得多。
  • RBarryYoung 是对的,这对于矢量类来说更简洁。此外,虽然这看起来很复杂,但这也是“现实世界是如何做到的”。习惯使用牛顿方程会让你做所有的运动算法更容易。
  • 嗯,它很好地近似于“现实世界是如何做到的”,并且在爱因斯坦提供替代方案之前,它经受了数百年的考验 :-)
  • 下划线过载。我的眼睛。
【解决方案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 值,您可以省去处理向量、角度和联立方程的麻烦。

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

玩得开心!

【讨论】:

    【解决方案3】:

    你需要用正确的物理术语来思考。你有一个速度,你想增加一个加速度。这就是它的全部 - 加速度是速度的逐渐变化,它将把敌人拉向玩家,让它超越,减速(或转弯)然后返回玩家。

    加速度测量为 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)
    

    【讨论】:

    • 那么显然将新速度“添加”到敌人的位置。 :-)
    • 我不确定我是否理解以下数学公式:速度 = 速度 *(最大幅度 / |速度|)。在 Vx = Vx + c * Ax 中,Ax 将是玩家-敌人向量的 X 分量,对吗?
    • 是的,在这个练习中,Ax 是玩家-敌人向量的 x 分量。更一般地说,Ax 是加速度的 x 分量,但使用玩家-敌人向量作为加速度就足够了。
    • Kirk's 与 Imagists 基本相同,只是 Kirk 封装(隐藏)了向量数学,而 Imagist 将其明确化。这就是为什么 Kirk 看起来很简单,但你无法计算出数学,而 Imagist 看起来太复杂,但数学很简单。
    • 感谢 RBarry - 这是真的,但我认为当我将其分解为 x 和 y 组件时,我会明确说明所需的计算。我只是没有展示所有的工作原理!
    【解决方案4】:

    前段时间我写了一个简单的小行星游戏,它有一艘“盟友”飞船,可以为你追捕小行星并向它们射击。基本上它找到了最近的小行星,然后开始平稳地转向它并追赶它。遗憾的是我没有代码了,但如果没记错的话,我想我每转一圈都会把船转一点,然后如果小行星很远,我会加速,但如果它很近,我会尝试匹配小行星的速度。实际上这很酷,而且涉及最少的代数。

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

    【讨论】:

      【解决方案5】:

      一种简单的方法(不是正确的物理方法)是计算敌人的“期望速度”,然后调整敌人当前的速度,注意顶部的任何限制或最低速度。

      例如,在我编写的一个小型 2d 游戏 (http://wordwarvi.sourceforge.net) 中,有“寻热导弹”。如果导弹停在半空中转身,这看起来很奇怪。所以我所做的如下:我计算一个朝向玩家的“期望速度”。这只是通过“相似三角形”完成的。我在 X 和 Y 中找到与玩家的距离,以较大者为准,我将“期望的(x 或 y)速度设为可能的最大值,然后缩放另一个以适应“相似三角形”。 ,这只是“所需速度”,而不是当前速度。我采用当前速度并将其缓慢(每帧一点点)调整为“所需”速度(尽管每帧也会重新计算所需速度,)注意 vx 和 vy 的最小值,以防止它们在半空中停止。

      愚蠢的算法,但它工作正常(没有人抱怨它们太容易、太难或太不切实际了。)

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

      【讨论】:

      • 如何计算调整?例如,如果目标速度距离“Y”比距离“X”更远,你是否起飞 x 并添加到 y?当你说“期望的 x 或 y”时,你的意思是基于对玩家来说更大的那个,对吗? (只是确认我认为你所说的)。
      • 为什么这是“不正确的物理学”?你所描述的几乎正是如果你通过改变推进器的方向来操纵宇宙飞船会发生什么。如果速度调整发生变化,您也在控制推进器的功率。如果它是恒定的,那么你的推进器就会卡在全功率。我怀疑这是美国宇航局实际解释的一个案例。 :-)
      • Adam Liss:这不是正确的物理学,因为我没有做 F = ma 之类的事情,例如,我的最大速度在 x 和 y 中是独立限制的,所以东西可以移动 sqrt(2 )*max 在对角线方向上,但仅在水平或垂直方向上最大。这是一个非常粗略的近似值,足以在视频游戏中获得乐趣。
      • cmpstudio:查找“相似三角形”。如果 X 和 Y 是到目标的距离,则通过相似的三角形 X/Y == dvx/dvy 其中 dvx,dvy 是所需的 x 和 y 速度。 X 或 Y 中的哪一个是最大幅度的,最大输出对应的 dvx 或 dvy。只剩下一个未知数。求解未知数的 X/Y == dvx/dvy。然后,每一帧,将对象当前速度调整为计算出的 dvx、dvy。这里有一些示例代码:wordwarvi.cvs.sourceforge.net/viewvc/wordwarvi/wordwarvi/… 在第 6300 行查看函数 missile_move。
      【解决方案6】:

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

      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 部分是独立的。

      现在,如果您想要一个无法打开一角钱但必须弯曲的搜索器,事情就会变得棘手......

      【讨论】:

        【解决方案7】:

        只有几个提示可以让这件事变得正确而简单。 1)使用向量是最简单和最通用的,而不是把所有东西都写两到三遍。 2) 如果你控制力(实际上是加速度,因为 A=F/mass),然后动态地演化速度和位置,事情就会看起来正确。

        您的真实运动的基本循环如下所示(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)。我建议你尝试这些,直到你得到与你所追求的物理外观和感觉相匹配的东西。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-11-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-14
          • 1970-01-01
          相关资源
          最近更新 更多