7

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

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

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

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

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

任何提示、公式或问题将不胜感激!谢谢堆栈溢出。

4

7 回答 7

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值,您可以省去处理向量、角度和联立方程的麻烦。

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

玩得开心!

于 2009-09-02T01:51:44.250 回答
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)
于 2009-09-02T02:20:29.407 回答
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是当前时间。

当然这是一条直线,所以如果你想转换成二维或三维,你必须做一些数学运算。 F, s, v, 和a都是向量,这意味着它们的方向同样重要。从技术上讲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)坐标,你就得到了你需要的力量。

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

于 2009-09-02T04:50:58.653 回答
1

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

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

于 2009-09-02T01:58:39.403 回答
1

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

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

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

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

于 2009-09-02T03:04:09.227 回答
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 部分是独立的。

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

于 2009-09-02T14:56:18.983 回答
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). 我建议你玩弄这些,直到你得到与你想要的物理外观和感觉相匹配的东西。

于 2009-09-03T04:42:04.703 回答