AI算法在2d游戏中“射击”目标

时间:2010-11-05 15:31:42

标签: algorithm math

在我的2D游戏中,我想创建一个可以“射击”给玩家的智能机器人。 假设我可以传递给我的机器人:

actual xEnemy, yEnemy

also enemy speed and angle direction

考虑到Bot必须将枪旋转到正确的方向,我如何计算“射击位置”?

对我来说这是一个非常大的问题,因为......我绝对不擅长数学! 提前感谢您的宝贵帮助!

3 个答案:

答案 0 :(得分:3)

你要问的是非平凡的。我将从最简单的解决方案开始,并对此进行扩展。

首先,假设你和你的敌人都是静止的。 你需要计算你和你的敌人之间的角度,旋转你的武器指向敌人,然后开火。使用您最喜欢的搜索引擎查找如何在平面上找到两个点之间的角度的描述(您说的是2D)。

编写完可以执行上述操作的代码后,请转到:

你的敌人以恒定的速度向恒定方向移动。你还是静止的。这是一个令人惊讶的难题。为了简化,我们假设你可以瞬间瞄准你的武器。

如果你知道你和敌人在哪里,以及敌人的速度和方向,那么你可以随时确定敌人的位置(以及他的距离和方向)。

你知道你的射弹有多快。因此,如果你从当前位置画一条线来拦截敌人预期行进路线上的任何位置,你可以确定你的射弹击中敌人需要多长时间。那么关键是找到敌人路径上的点,如果你立即发射射弹,它会在适当的时间与敌人相交。这通常需要连续近似。

如果你不能瞬间旋转你的武器,那么问题变得更加困难,因为旋转你的武器指向敌人所需的时间取决于敌人的行进速度和方向。需要更多近似值。

当你和敌人都在移动时,事情变得更加复杂,尽管你可以构建数学,以便“保持自己”。也就是说,对敌人的速度和轨迹进行转换,以反映敌人相对于你的移动方式。然后数学与你静止的情况相同。

数学本身至多是基本三角学。您需要知道如何计算两点之间的距离,线与点之间的距离,两点之间的角度,给定起点和方向的线上的计算点,以及如何围绕任意点旋转。所有这些都是众所周知的问题,在网上有很多很好的例子。不过,你必须做一些研究才能找到它们。

可能你最好的办法是找一个好的计算机图形教程。

答案 1 :(得分:2)

表示法:我用大写字母书写矢量,用小写字母书写标量,用矢量V用x轴书写角度∠V。 (您可以使用多种语言的函数atan2进行计算。)

最简单的情况是固定式射击游戏,它可以立即旋转。

让目标位于A位置并以速度VA移动,并且射击者在位置B处静止并且可以以速度 s 发射子弹。让射手在时间0开火。子弹击中时间 t 使得| A - B + t VA | = t s 。这是 t 中的一个简单的二次方程式,您应该可以轻松地解决(或确定没有解决方案)。确定 t 之后,您现在可以计算出触发角度,它只是∠(A - B + t VA)。

现在假设射手不是静止但是具有恒定的速度VB。 (我在这里假设牛顿相对论,即子弹速度加到了射击者的速度上。)

它仍然是一个简单的二次方程式来计算时间:| A - B + t (VA - VB)| = t s 。在这种情况下,触发角为∠(A - B + t (VA - VB))。

如果射手在射击前等到 u 时间怎么办?然后当| A - B + t (VA - VB)|时,子弹击中目标=( t - u s 。触发角仍为∠(A - B + t (VA - VB))。

现在为您解决问题。假设射击者可以在时间 r 中完成半个旋转。然后它肯定会在 r 时开火。 (基本上:如上所述,在 r 时计算出必要的射击角度(如果有的话),旋转到该角度,停止,等到时间 r ,然后开火。)

但你可能想知道射手可以射击的最早时间。在这里你可能想要使用逐次逼近来找到它。 (算法草图:你能在0时开火吗?不能。你可以在 r 时开火吗?是的。你可以在时间½ r 开火吗?等等。)

答案 2 :(得分:0)

"简单"解决这个问题的方法。如果需要旋转的枪 NOT ,则需要多次询问和回答。我找到了this post的最佳答案(Jeffrey Hantin有一个很好的解释)。

正如Jim Mischel所说,旋转案件的答案甚至不是一件小事。这是因为您需要做的旋转量是目标的拦截位置(发生碰撞的位置)的函数。我不知道是否有一个干净的封闭形式解决方案(似乎不太可能给出公式),但我能够通过向后工作并迭代到解决方案来解决它。

基本思想是这样的:

假设您的"实体"将从当前面向位置旋转到面向拦截位置,然后立即开火。

  • 选择一个影响时间。
  • 如果目标以恒定速度移动,则计算目标的最终位置。
  • 计算弹丸前往该位置所需的时间。
  • 计算实体旋转以面向拦截位置所需的时间。
  • 计算冲击时间与旋转/行程时间的差异。
  • 调整上下冲击时间,使每次差异变小(例如二进制搜索)。
  • 当你足够接近时,你就完成了。否则,再次开始计算。

在简单的情况下,如果您有0,1或2个解决方案并且选择最佳解决方案,您可以从二次方判别中得知。我不认为你可以在这里保证,但是你可以限制你愿意搜索的时间范围以及你要搜索的迭代次数。这在实践中非常有效。

由于我无法在网上找到解决方案,我将发布我的。我写了一个函数来具体处理这个问题。 I have a blog entry on the entire calculation here。还有nice video showing it here

守则:

/* Calculate the future position of a moving target so that 
 * a turret can turn to face the position and fire a projectile.
 *
 * This algorithm works by "guessing" an intial time of impact
 * for the projectile 0.5*(tMin + tMax).  It then calculates
 * the position of the target at that time and computes what the 
 * time for the turret to rotate to that position (tRot0) and
 * the flight time of the projectile (tFlight).  The algorithms
 * drives the difference between tImpact and (tFlight + tRot) to 
 * zero using a binary search. 
 *
 * The "solution" returned by the algorithm is the impact 
 * location.  The shooter should rotate towards this 
 * position and fire immediately.
 *
 * The algorithm will fail (and return false) under the 
 * following conditions:
 * 1. The target is out of range.  It is possible that the 
 *    target is out of range only for a short time but in
 *    range the rest of the time, but this seems like an 
 *    unnecessary edge case.  The turret is assumed to 
 *    "react" by checking range first, then plot to shoot.
 * 2. The target is heading away from the shooter too fast
 *    for the projectile to reach it before tMax.
 * 3. The solution cannot be reached in the number of steps
 *    allocated to the algorithm.  This seems very unlikely
 *    since the default value is 40 steps.
 *
 *  This algorithm uses a call to sqrt and atan2, so it 
 *  should NOT be run continuously.
 *
 *  On the other hand, nominal runs show convergence usually
 *  in about 7 steps, so this may be a good 'do a step per
 *  frame' calculation target.
 *
 */
bool CalculateInterceptShotPosition(const Vec2& pShooter,
                                    const Vec2& vShooter,
                                    const Vec2& pSFacing0,
                                    const Vec2& pTarget0,
                                    const Vec2& vTarget,
                                    float64 sProjectile,
                                    float64 wShooter,
                                    float64 maxDist,
                                    Vec2& solution,
                                    float64 tMax = 4.0,
                                    float64 tMin = 0.0
                                    )
{
   cout << "----------------------------------------------" << endl;
   cout << " Starting Calculation [" << tMin << "," << tMax << "]" << endl;
   cout << "----------------------------------------------" << endl;

   float64 tImpact = (tMin + tMax)/2;
   float64 tImpactLast = tImpact;
   // Tolerance in seconds
   float64 SOLUTION_TOLERANCE_SECONDS = 0.01;
   const int MAX_STEPS = 40;
   for(int idx = 0; idx < MAX_STEPS; idx++)
   {
      // Calculate the position of the target at time tImpact.
      Vec2 pTarget = pTarget0 + tImpact*vTarget;
      // Calulate the angle between the shooter and the target
      // when the impact occurs.
      Vec2 toTarget = pTarget - pShooter;
      float64 dist = toTarget.Length();
      Vec2 pSFacing = (pTarget - pShooter);
      float64 pShootRots = pSFacing.AngleRads();
      float64 tRot = fabs(pShootRots)/wShooter;
      float64 tFlight = dist/sProjectile;
      float64 tShot = tImpact - (tRot + tFlight);
      cout << "Iteration: " << idx
      << " tMin: " << tMin
      << " tMax: " << tMax
      << " tShot: " << tShot
      << " tImpact: " << tImpact
      << " tRot: " << tRot
      << " tFlight: " << tFlight
      << " Impact: " << pTarget.ToString()
      << endl;
      if(dist >= maxDist)
      {
         cout << "FAIL:  TARGET OUT OF RANGE (" << dist << "m >= " << maxDist << "m)" << endl;
         return false;
      }
      tImpactLast = tImpact;
      if(tShot > 0.0)
      {
         tMax = tImpact;
         tImpact = (tMin + tMax)/2;
      }
      else
      {
         tMin = tImpact;
         tImpact = (tMin + tMax)/2;
      }
      if(fabs(tImpact - tImpactLast) < SOLUTION_TOLERANCE_SECONDS)
      {  // WE HAVE A WINNER!!!
         solution = pTarget;
         return true;
      }
   }
   return false;
}