Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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 http://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);
    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.
share|improve this question
1  
"Leading the target" may be the phrase you're looking for. –  Byte56 Sep 11 '12 at 16:37
add comment

2 Answers

up vote 10 down vote accepted

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 

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

share|improve this answer
    
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
add comment

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.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.