「シューター」が回転せず、発射体を発射してターゲットを迎撃するだけでよい場合は、この投稿のジェフリー・ハンティンによる優れた回答をご覧になることをお勧めします。
一方、体が回転してから発砲し、発射物がターゲットを迎撃したい場合...それは非常に別の問題です. 要するに、上記の投稿で見つけたのと同じ 2 つの方程式から始まりますが、射手の向きのベクトルと目標とする最終的な位置との間の角度の弧を表す別の方程式を追加します。これは、やや厄介な非線形で非二次的な方程式のセットにつながります。私はそれらを解決しませんでしたが、バイナリ検索を使用して答えを「絞り出しました」(または失敗しました)。
ブログ投稿には、写真とビデオの良さとともに、はるかに長く詳細な説明がありますが、ここに関数を (寛大なコメントと共に) 投稿します。
これが、私が思いついた基本的な説明を含む関数です。
/* 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;
}