- Non-Rotating Shooter
- Rotating Shooter

- The position of the shooter when the projectile will be launched: \(\vec{P_s}\)
- The position of the target when the shooter will launch the projectile (i.e. now): \(\vec{P_T^0}\)
- The speed at which your projectiles travel: \(S_b\)
- The velocity of the target, \(\vec{v_T}\)

## The Problem Space

Consider the image below:(1) \(\vec{P_T^1} = \vec{P_T^0} + \vec{v_T} * t_B\)

Since the projectile will have traveled for the same amount of time, it will have moved from \(\vec{P_s}\) to \(\vec{P_T^1}\) as well. In that time, it will have moved a distance of \(S_B x t_B\). Since we are talking about vector quantities here, we can write this as:

\(\mid\vec{P_T^1}-\vec{P_s}\mid = S_b * t_B\)

If we square both sides and break it into components to get rid of the absolute value:

(2) \((P_{Tx}^1 - P_{Sx})^2 +(P_{Ty}^1 - P_{Sy})^2 = S_b^2 * t_B^2\)

Breaking (1) into components as well and substituting back into (2) for the value of \(P_{Tx}^1\) and \(P_{Ty}^1\), we get the following:

\((P_{T0x} - P_{Sx} + v_{Tx}t_B)^2 + (P_{T0y} - P_{Sy} + v_{Ty}t_B)^2 = S_b^2 * t_B^2\)

For the sake of simplicity, we going to redefine:

\(P_T^0 - P_s = R \)(this is a constant)

After some algebra, this gives us the **final** equation:

\(t_B^2(v_{Tx}^2 + v_{Ty}^2-S_B^2) + t_B(2*R_x*v_{Tx} + 2*R_y*v_{Ty}) + (R_x^2 + R_y^2) = 0\)

This is a quadratic in \(t_B\):

\(t_b = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}\)

where:\( a =v_{Tx}^2 + v_y^2-S_B^2\)

\( b =2(R_x*v_{Tx} + R_y*v_{Ty})\)

\( c = R_x^2 + R_y^2\)

You can test the discriminant, \(b^2-4ac\):

< 0 \(\Rightarrow\) No Solution.

= 0 \(\Rightarrow\) One solution.

> 0 \(\Rightarrow\) Two solutions, pick the lowest positive value of \(t_B\).

Once you have solved the quadratic for \(t_B\), you can then substitute it back into (1) and calculate the intercept position, \(\vec{P_T^1}\).

# The Code

Putting this together and covering some edge cases:```
/* Calculate the future position of a moving target so that
* a projectile launched immediately can intercept (collide)
* with it.
*
* Some situations where this might be useful for an AI to
* make this calculation.
*
* 1. Shooting a projectile at a moving target.
* 2. Launching a football or soccer ball to a player.
* 3. Figuring out the best position to jump towards in
* a platform game.
*
*
* The output value, solution, is the position that the
* intercept will occur at and the location that the
* projectile should be launched towards.
*
* The function will return false if a solution cannot
* be found. Consider the case of a target moving away
* from the shooter faster than the speed of the
* projectile and you will see at least one case where
* this calculation may fail.
*/
bool CalculateInterceptShotPosition(const Vec2& pShooter,
const Vec2& pTarget0,
const Vec2& vTarget,
float64 sProjectile,
Vec2& solution
)
{
// This formulation uses the quadratic equation to solve
// the intercept position.
Vec2 R = pTarget0 - pShooter;
float64 a = vTarget.x*vTarget.x + vTarget.y*vTarget.y - sProjectile*sProjectile;
float64 b = 2*(R.x*vTarget.x + R.y*vTarget.y);
float64 c = R.x*R.x + R.y*R.y;
float64 tBullet = 0;
// If the target and the shooter have already collided, don't bother.
if(R.LengthSquared() < 2*DBL_MIN)
{
return false;
}
// If the squared velocity of the target and the bullet are the same, the equation
// collapses to tBullet*b = -c. If they are REALLY close to each other (float tol),
// you could get some weirdness here. Do some "is it close" checking?
if(fabs(a) < 2*DBL_MIN)
{
// If the b value is 0, we can't get a solution.
if(fabs(b) < 2*DBL_MIN)
{
return false;
}
tBullet = -c/b;
}
else
{
// Calculate the discriminant to figure out how many solutions there are.
float64 discriminant = b*b - 4 * a * c;
if(discriminant < 0)
{ // All solutions are complex.
return false;
}
if (discriminant > 0)
{ // Two solutions. Pick the smaller one.
// Calculate the quadratic.
float64 quad = sqrt(discriminant);
float64 tBullet1 = (-b + quad)/(2*a);
float64 tBullet2 = (-b - quad)/(2*a);
if((tBullet1 < 0) && (tBullet2 < 0))
{ // This would be really odd.
// Both times are negative.
return false;
}
else if(tBullet2 < 0 && tBullet1 >= 0)
{ // One negative, one positive.
tBullet = tBullet1;
}
else if(tBullet1 < 0 && tBullet2 >= 0)
{ // One negative, one positive.
tBullet = tBullet2;
}
else if(tBullet1 < tBullet2)
{ // First less than second
tBullet = tBullet1;
}
else
{ // Only choice left
tBullet = tBullet2;
}
}
else
{
tBullet = -b / (2*a);
}
}
// If the time is negative, we can't get there from here.
if(tBullet < 0)
{
return false;
}
// Calculate the intercept position.
solution = pTarget0 + tBullet*vTarget;
return true;
}
```

I have posted a working solution, with a simulation of using the above function and which you can tinker with, on github.