# Algorithm to shoot at a target in a 3d game

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) -
ENG_Math.sqr(projectileSpeed);
float b = 2.0f * (targetVelocity.x * targetPos.x +
targetVelocity.y * targetPos.y);
float c = ENG_Math.sqr(targetPos.x) + ENG_Math.sqr(targetPos.y);


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.
• "Leading the target" may be the phrase you're looking for. – MichaelHouse Sep 11 '12 at 16:37

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;
}
else
{
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


• You're a genius and saved my ass!! Damn I need a 15 reputation to upvote.... – Sebastian Bugiu Sep 13 '12 at 0:16
• @SebastianBugiu i did it for you. – AgentFire Nov 1 '12 at 10:37
• @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. – Steve H Nov 1 '12 at 15:03

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.