1
\$\begingroup\$

I am trying to do a simple AI Controller, which fires Missiles at the surrounding targets in the Scene.

  • The AI Controller can fire projectiles when moving or stationary.
  • The Targets are either stationary or move around in the scene with a Constant Velocity and can't fire projectiles.

I did some searching on Stack overflow and came up with this code to find the direction the AI controller must fire the projectile (constant speed) to hit a target travelling at a constant velocity(it can also be stationary):

private bool GetProjectileDirection(GObject target, GObject source, out Vector3 direction)
{
    // Initialize direction to Zero
    direction = Vector3.zero;

    // The Relative Position between the AI Controller and the target.
    Vector2 w = new Vector2(target.Position.x - source.Position.x, target.Position.y - source.Position.y);

    // The Relative Velocity between the source and the target.
    Vector2 v = new Vector2(target.Velocity.x - source.Velocity.x, target.Velocity.y - source.Velocity.y);

    // Quadratic Equation Co-efficients
    float a = Vector2.Dot(v, v) - BULLET_SPEED * BULLET_SPEED;
    float b = Vector2.Dot(w, v);
    float c = Vector2.Dot(w, w);

    float root = (b * b) - (a * c);

    // The Problem seems to occur here as this becomes less than zero most of the time,
    // and exits the function.
    // But on the screen, the object is well within the range for the AI to fire at it
    if (root < 0)
        return false;

    // If root < 0, then this becomes NaN and brings the simulation to a crawl
    double t = (-b - Math.Sqrt(root)) / a;

    double shootX = w.x + t * v.x;
    double shootY = w.y + t * v.y;

    double theta = Math.Atan2(shootY, shootX);
    direction = BULLET_SPEED * new Vector3(Math.Cos(theta), 0, Math.Sin(theta));

    return true;
}

I am pretty sure I am missing something. I just can't pinpoint what exactly is it. As a result, the AI seems to miss most of the targets around it.

\$\endgroup\$
  • \$\begingroup\$ Looks like you may have forgotten to add the launcher's velocity to its projectile on firing. \$\endgroup\$ – DMGregory Sep 22 '19 at 12:30
  • \$\begingroup\$ @DMGregory But most of the time the Execution doesn't even reach the part of the Code. The code returns at the line if(root < 0). \$\endgroup\$ – Vignesh Gunasekaran Sep 22 '19 at 12:34
  • 1
    \$\begingroup\$ That usually means your bullet speed is too low. We can check the math though to see if there's an error in the formula. \$\endgroup\$ – DMGregory Sep 22 '19 at 12:36
  • \$\begingroup\$ Okay! Let me check! \$\endgroup\$ – Vignesh Gunasekaran Sep 22 '19 at 12:36
  • \$\begingroup\$ @DMGregory Okay! You were right. Increasing the Bullet speed resulted in the Projectiles being fired! But the Accuracy is all over the place! \$\endgroup\$ – Vignesh Gunasekaran Sep 22 '19 at 12:41
2
\$\begingroup\$

This is the updated code, with code for handling some edge cases. Also, I made sure the BULLET_SPEED large enough for the Equations to work:

 private bool GetProjectileDirection(GObject target, GObject source, out Vector3 direction)
{
    direction = Vector3.Zero;

    Vector2 w = new Vector2(target.Position.x - source.Position.x, target.Position.y - source.Position.y);
    float ww = Vector2.Dot(w, w);
    float r = target.BoundingRadius;

    Vector2 v = new Vector2(target.Velocity.x - source.Velocity.x, target.Velocity.z - source.Velocity.z);

    double a = Vector2.Dot(v, v) - (BULLET_SPEED * BULLET_SPEED);
    double b = 2 * Vector2.Dot(w, v);
    double c = Vector2.Dot(w, w);

    double h = -b / (2 * a);
    double k2 = h * h - (c / a);

    double t = 0.0f;

    if (k2 < 0)
        return false;

    if (k2 == 0)
    {
        if (h > 0)
            t = h;
        else
            return false;
    }

    if (k2 > 0)
    {
        double k = Math.Sqrt(k2);
        double r0 = h - k;
        double r1 = h + k;

        if (r0 > 0)
            t = r0;

        else if (r1 > 0)
            t = r1;

        else
            return false;
    }

    double shootX = w.x + t * v.x;
    double shootY = w.y + t * v.y;

    double theta = Math.Atan2(shootY, shootX);
    direction = new Vector3(Math.Cos(theta), 0, Math.Sin(theta));

    return true;

}

Here is the answer that I based this code off.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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