For those of you remembering Descent Freespace it had a nice feature to help you aim at the enemy when shooting non-homing missiles or lasers: it showed a crosshair in front of the ship you chased telling you where to shoot in order to hit the moving target.

I tried using the answer from https://stackoverflow.com/questions/4107403/ai-algorithm-to-shoot-at-a-target-in-a-2d-game?lq=1 but it's for 2D so I tried adapting it.

I first decomposed the calculation to solve the intersection point for XoZ plane and saved the x and z coordinates and then solving the intersection point for XoY plane and adding the y coordinate to a final xyz that I then transformed to clipspace and put a texture at those coordinates. But of course it doesn't work as it should or else I wouldn't have posted the question.

From what I notice the after finding x in XoZ plane and the in XoY the x is not the same so something must be wrong.

    float a = ENG_Math.sqr(targetVelocity.x) + ENG_Math.sqr(targetVelocity.y) -
    float b = 2.0f * (targetVelocity.x * targetPos.x + 
            targetVelocity.y * targetPos.y);
    float c = ENG_Math.sqr(targetPos.x) + ENG_Math.sqr(targetPos.y);
    ENG_Math.solveQuadraticEquation(a, b, c, collisionTime);

First time targetVelocity.y is actually targetVelocity.z (the same for targetPos) and the second time it's actually targetVelocity.y.

The final position after XoZ is

    crossPosition.set(minTime * finalEntityVelocity.x + finalTargetPos4D.x, 0.0f, 
                minTime * finalEntityVelocity.z + finalTargetPos4D.z);

and after XoY

    crossPosition.y = minTime * finalEntityVelocity.y + finalTargetPos4D.y;

Is my approach of separating into 2 planes and calculating any good? Or for 3D there is a whole different approach?

  • sqr() is square not sqrt - avoiding a confusion.
  • 2
    \$\begingroup\$ "Leading the target" may be the phrase you're looking for. \$\endgroup\$
    – House
    Commented Sep 11, 2012 at 16:37

2 Answers 2


There is no need to break it down into 2 2d functions. That quadratic equation you are working with works fine in 3d as well. Here is pseudo code for either 2d or 3d. It implies a tower (tower defense) is shooting the projectile:

Vector totarget =  target.position - tower.position;

float a = Vector.Dot(target.velocity, target.velocity) - (bullet.velocity * bullet.velocity);
float b = 2 * Vector.Dot(target.velocity, totarget);
float c = Vector.Dot(totarget, totarget);

float p = -b / (2 * a);
float q = (float)Math.Sqrt((b * b) - 4 * a * c) / (2 * a);

float t1 = p - q;
float t2 = p + q;
float t;

if (t1 > t2 && t2 > 0)
    t = t2;
    t = t1;

Vector aimSpot = target.position + target.velocity * t;
Vector bulletPath = aimSpot - tower.position;
float timeToImpact = bulletPath.Length() / bullet.speed;//speed must be in units per second 

'aimSpot' may be the vector you are asking about.

  • \$\begingroup\$ You're a genius and saved my ass!! Damn I need a 15 reputation to upvote.... \$\endgroup\$ Commented Sep 13, 2012 at 0:16
  • \$\begingroup\$ @SebastianBugiu i did it for you. \$\endgroup\$
    – AgentFire
    Commented Nov 1, 2012 at 10:37
  • \$\begingroup\$ @SebastianBugiu Thanks, I was glad when I learned this concept and am glad its helped you. Another elegant feature of it is that you don't need to mess around with collision detection algorithms. No CD code need be written. Since target and projectile paths are predictable, the impact WILL occur when timeToImpact counts down to zero. \$\endgroup\$
    – Steve H
    Commented Nov 1, 2012 at 15:03
  • \$\begingroup\$ If the bullet and target have exactly the same speed, a will be zero and this will result in division by zero. But in that case you can just solve for a linear equation instead of a quadratic one: t = -c/b; Also, I'm pretty sure bullet.velocity (in the calculation of a) should be bullet.speed. \$\endgroup\$ Commented Jan 24, 2021 at 0:41
  • \$\begingroup\$ Also, you should use t2 instead of t1 if t1 < 0. You also might want to check if they're both negative and do something like withholding fire or shooting without trying to lead the target in that case. \$\endgroup\$ Commented Jan 24, 2021 at 1:17

There is also a good blog post about same subject: http://playtechs.blogspot.kr/2007/04/aiming-at-moving-target.html. It also contains more complex samples that include gravity.

The author has done more simplification, which results in more compact code:

double time_of_impact(double px, double py, double vx, double vy, double s)
    double a = s * s - (vx * vx + vy * vy);
    double b = px * vx + py * vy;
    double c = px * px + py * py;

    double d = b*b + a*c;

    double t = 0;
    if (d >= 0)
        t = (b - sqrt(d)) / a;
        if (t < 0) 
            t = (b + sqrt(d)) / a;
            if (t < 0)
                t = 0;

    return t;

Update: Original author took into account only bigger root. But in case of smaller root being non-negative, it results in better solution, since time of impact is smaller. I have updated the code correspondingly.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .