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\$
6
  • \$\begingroup\$ Looks like you may have forgotten to add the launcher's velocity to its projectile on firing. \$\endgroup\$
    – DMGregory
    Sep 22, 2019 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\$ Sep 22, 2019 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, 2019 at 12:36
  • \$\begingroup\$ Okay! Let me check! \$\endgroup\$ Sep 22, 2019 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\$ Sep 22, 2019 at 12:41

1 Answer 1

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.

\$\endgroup\$

You must log in to answer this question.

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