Whether it’s for a space shooting game or if you’ve been tasked with defending a secure location, it’s possible you need to learn how to program a turret that can shoot a moving target. If that’s the case, and you’re somehow stuck in the math, this short article is here to help!

First let’s visualize the situation. We’ll have a turret that can rotate in any direction to shoot the bullet to hit its target, which is moving with a constant arbitrary velocity.

Our Turret (blue square) and its moving target (red circle).

You may possibly be inclined to use polar coordinates to write the distances and directions. While the problem can be solved using angles and trigonometric functions, I find it simpler to use cartesian coordinates. Not to mention trigonometric functions are usually slower to compute.

For simplicity, we will assume the turret is located at the origin and does not move. It is likely this assumption does not hold for your situation. In that case you can simply subtract the turret’s position from the target’s position, and likewise subtract the turret’s velocity from the target’s velocity. That is, we only care about the relative position and velocity of the target with respect to the turret.

With that out of the way, let’s write down what we know.

The target’s position as a function of time is:

xtarget = vx,target t + x0,target
ytarget = vy,target t + y0,target

The target’s path

We can calculate the target’s position at any time by knowing its initial position (x0, y0) and velocity (vx, vy), both of which we should already know.

The bullet we will shoot out of our turret will follow the same equations we wrote above for the target. However, we do not know the velocity (vx, vy) of our bullet, since those depend on which direction we fire our turret. However, what we do know is the absolute velocity (or speed) of our bullet, which will be denoted |vbullet|.

Ahah! Then, even though we cannot yet say which direction our bullet will go, we can say how far away it will be at a given time.

The distance, r, of the bullet away from the turret is

rbullet = |vbullet| t

Additionally, we can write the distance of the bullet from the turret in terms of its position, x and y. Simply recall the formula for a circle:

r2 = x2 + y2

We can put these two formulas together to get an equation for the bullet’s distance from the turret in terms of its position (x, y):

(|vbullet| t)2 = xbullet2 + ybullet2

A constraint for the bullet’s path. Nice!

Now we have equations to describe the path of the target and the path of the bullet. Let’s write them below:

xtarget = vx,target t + x0,target
ytarget = vy,target t + y0,target
xbullet2 + ybullet2 = (|vbullet| t)2

In order for the bullet to hit the target, we need to find a solution to these equations in which the target’s position and the bullet’s position are the same. In math terms, we want the following constraint:

xtarget = xbullet
ytarget = ybullet

That means our system of equations gets simplified to

x = vx,target t + x0,target
y = vy,target t + y0,target
x2 + y2 = (|vbullet| t)2

Our system of equations

We have 3 equations and 3 unknowns (x, y, and t). Looks like we should be able to solve it!

If we substitute x and y into the bottom equation we’ll only have to solve for t, and then we can use t to find x and y. Let’s try that!

The combined equation is:

(vx t + x0)2 + (vy t + y0)2 = (|vbullet| t)2

Not so pretty, but we can rearrange it into:

(vx2 + vy2 - vbullet2) t2 + 2(vx x0 + vy y0) t + x2 + y2 = 0

Which, if you look at it long enough, has the form:

at2 + bt + c = 0

Where

a = vx2 + vy2 - vbullet2
b = 2(vx x0 + vy y0)
c = x2 + y2

You likely learned how to solve this in high school using the quadratic equation:

t = (-b +- sqrt(b2 - 4 a c)) / (2 a)

If a ends up being 0 then you have an easy-peasy linear equation. The quadratic formula will blow up in our face if you apply it to a linear equation, so we handle this case separately (t = -c / b).

You might also remember that a quadratic function can have 0, 1, or 2 solutions. In other words, it might be impossible to hit our target, and we might be able to shoot in two different directions and still hit it. Unfortunately, if we can’t get a value for t (or if the solution is imaginary) we’re out of luck!

“How can we have 0 or 2 solutions?” you might ask. If you didn’t ask that, you can skip the rest of this paragraph; otherwise read along. Well, if the target is flying away from our turret, and it travels faster than our bullets, then, unless we can bend space-time to our will, the bullet won’t be able to catch up and there won’t be a valid solution to our problem. We can have 2 solutions if our target travels faster than our bullet AND our target is moving closer to the turret: either 1) we hit the target as it comes towards us (the more likely choice), or 2) we shoot the bullet away from where the target is, but where the target will catch up with it and hit it (the less likely, and more silly, but still possible choice).

If we get two values for t, we likely want the smaller one since we’d like to hit our target as soon as possible. However, t must not be negative, or else it would mean our bullet would need to travel back in time in order to hit.

Once we have our t, though, the rest is a piece of cake. We just plug in t to the first two equations to obtain the values of x and y where the hit occurs, we aim the turret in that direction, and then we fire!

It’s over! Below is a Javascript implementation of the steps described.

/*
 * x0 - target's initial x position
 * y0 - target's initial y position
 * vx - target's initial x velocity
 * vy - target's initial y velocity
 * vb - bullet's speed
 */
function shootTarget(x0, y0, vx, vy, vb) {
  // solve quadratic equation
  var a = vx*vx + vy*vy - vb*vb;
  var b = 2 * (x0*vx + y0*vy);
  var c = x0*x0 + y0*y0;
  var determinant = b*b - 4*a*c;
  var t;

  if (a === 0) {
    // we have a linear equation
    t = -c / b;
  } else {
    // if the determinant is negative, the solution is imaginary
    // and we cannot hit our target
    if (determinant < 0) return;

    var t1 = (-b + Math.sqrt(determinant)) / (2*a);
    var t2 = (-b - Math.sqrt(determinant)) / (2*a);

    // Get the smallest t, where t >= 0
    // if t < 0 then the solution is in the past
    if (t1 >= 0 && t2 >= 0) {
      t = Math.min(t1, t2);
    } else if (t1 >= 0) {
      t = t1;
    } else if (t2 >= 0) {
      t = t2;
    } else {
      return;
    }
  }

  // find where the target will be at time t
  var x_t = vx*t + x0;
  var y_t = vy*t + y0;

  // now we know which direction to aim!
  // we can specify it as an angle = atan2(y, x), or as a unit vector
  var magnitude = Math.sqrt(x_t*x_t + y_t*y_t);
  var ux = x_t / magnitude;
  var uy = y_t / magnitude;
  // fire the bullet in the calculated direction with the given speed
  fireBullet(ux*vb, uy*vb);
}