如果您的“射手”不旋转,而您只需要发射弹丸以拦截目标,我建议您查看 Jeffery Hantin 在这篇文章中的出色回答。
另一方面,如果您的身体旋转然后射击并且您希望弹丸拦截目标......这是一个非常不同的问题。简而言之,它从您在上面的帖子中找到的两个方程式开始,但是您添加了另一个方程式,用于射手的面向矢量和您瞄准的最终位置之间的角度的弧度。这导致了一些令人讨厌的非线性和非二次方程组。我没有解决它们,但我确实使用了二进制搜索来“挤出”答案(或失败)。
我在博客文章中有更长更详细的描述,以及图片和视频的优点,但我会在此处发布该功能(带有慷慨的评论)。
这是我想出的带有基本描述的功能:
/* 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;
}