Jun 212013
 

In an old post I explained how to shoot an object to hit a moving target in 2D. The method in 3D is basically the same, but the code below is much cleaner and might be simpler to understand even for the 2D case.

Unity3D example and source code

Unity webplayer example

Unity project

The interesting bit of C#:

private Vector3 FindInterceptVector(Vector3 shotOrigin, float shotSpeed,
    Vector3 targetOrigin, Vector3 targetVel) {
   
    Vector3 dirToTarget = Vector3.Normalize(targetOrigin - shotOrigin);
   
    // Decompose the target's velocity into the part parallel to the
    // direction to the cannon and the part tangential to it.
    // The part towards the cannon is found by projecting the target's
    // velocity on dirToTarget using a dot product.
    Vector3 targetVelOrth =
    Vector3.Dot(targetVel, dirToTarget) * dirToTarget;
   
    // The tangential part is then found by subtracting the
    // result from the target velocity.
    Vector3 targetVelTang = targetVel - targetVelOrth;
   
    /*
    * targetVelOrth
    * |
    * |
    *
    * ^...7  <-targetVel
    * |  /.
    * | / .
    * |/ .
    * t--->  <-targetVelTang
    *
    *
    * s--->  <-shotVelTang
    *
    */

   
    // The tangential component of the velocities should be the same
    // (or there is no chance to hit)
    // THIS IS THE MAIN INSIGHT!
    Vector3 shotVelTang = targetVelTang;
   
    // Now all we have to find is the orthogonal velocity of the shot
   
    float shotVelSpeed = shotVelTang.magnitude;
    if (shotVelSpeed > shotSpeed) {
        // Shot is too slow to intercept target, it will never catch up.
        // Do our best by aiming in the direction of the targets velocity.
        return targetVel.normalized * shotSpeed;
    } else {
        // We know the shot speed, and the tangential velocity.
        // Using pythagoras we can find the orthogonal velocity.
        float shotSpeedOrth =
        Mathf.Sqrt(shotSpeed * shotSpeed - shotVelSpeed * shotVelSpeed);
        Vector3 shotVelOrth = dirToTarget * shotSpeedOrth;
       
        // Finally, add the tangential and orthogonal velocities.
        return shotVelOrth + shotVelTang;
    }
}

Update:

If you want to find the point where they meet, you can calculate the time it will take, and then multiply the shot velocity by that. In practice they will collide sooner since they have a certain radius, but we can take that into account when we calculate the time.

// Find the time of collision (distance / relative velocity)
float timeToCollision = ((shotOrigin - targetOrigin).magnitude - shotRadius - targetRadius)
        / (shotVelOrth.magnitude-targetVelOrth.magnitude);

// Calculate where the shot will be at the time of collision
Vector3 shotVel = shotVelOrth + shotVelTang;
Vector3 shotCollisionPoint = shotOrigin + shotVel * timeToCollision;
Jan 282011
 

I have been playing with target intersection prediction after I played a game called Beacon, where the enemies predict where you are going. After a bit of struggle, I got it to work accurately. This post at stackoverflow helped. I’m going to explain how it works more in depth.

Assumptions
Here we have a cannon on the ground, trying to hit a flying target. Let us assume the target is travelling with constant velocity, unaffected by gravity, and so is the projectile shot by the cannon.

The target will have a known velocity u. We will also know the speed of the projectile |v| in advance, and we want to find the vector v that will make it hit the target.

Method
The idea is to transform the shooter and target to a new coordinate system where the line between them is one of the axes, match up the parallell speed, and then calculate the appropriate tangential speed for the projectile.

In order for the projectile to hit the target, the I composants of the velocities, vi and ui must be equal. If they are not, the projectile will be too fast or slow in that direction and cannot hit the target.

To calculate vi, first find the vector between A and B, and normalize it. Then project u onto AB using a dot product, giving uj. Subtracting uj from u, we get ui which is equal to vi.

Once vi has been determined, |vj| can be found by using Pythagoras theorem, since we know the magnitude |v|.

Then we can multiply the unit vector AB by |vj| to get vj, and lastly, add together vj and vi to get v

Pseudocode

// Given: ux, uy, vmag (projectile speed), Ax, Ay, Bx, By

// Find the vector AB
ABx = Bx - Ax
ABy = By - Ay

// Normalize it
ABmag = sqrt(ABx * ABx + ABy * ABy)
ABx /= ABmag
ABy /= ABmag

// Project u onto AB
uDotAB = ABx * ux + ABy * uy
ujx = uDotAB * ABx
ujy = uDotAB * ABy

// Subtract uj from u to get ui
uix = ux - ujx
uiy = uy - ujy

// Set vi to ui (for clarity)
vix = uix
viy = uiy

// Calculate the magnitude of vj
viMag = sqrt(vix * vix + viy * viy)
vjMag = sqrt(vMag * vMag - viMag * viMag)

// Get vj by multiplying it's magnitude with the unit vector AB
vjx = ABx * vjMag
vjy = ABy * vjMag

// Add vj and vi to get v
vx = vjx + vix
vy = vjy + viy

It can be interesting to note that any vector v with vi=ui, and vj in the direction of the target, will make the projectile hit the target. For example the two purple lines in the diagram. But the only one with the correct magnitude is the v we have found.

This example is in 2D, but the same method can be used in 3D by considering the plane in which the target, cannon and target velocity lies.

Corner Cases
It is not always certain that the cannon can hit the target. This can be the case if the target is faster than the projectile, and moving away from it. For the hit to be possible u must be greater than vi at the time of fire. If u<vi the expression in the square root will be negative, and thus the root will be undefined, so you should have a check for that.

Example
Flash example

Source code
Flashdevelop project

Update:

I have updated this article with some pseudo code, and to be much easier to understand.

Update 2, the same thing in 3D:

Unity3D example and source code