4
\$\begingroup\$

Well I have a simple 2D game where all positions are defined by and X and Y coordinate.

There are objects moving at a constant velocity. And a turret fires a bullet at the X and Y coordinates of the object. By the time the bullet goes to the desired area, the object has already moved.

So i need to adjust the X and Y target positions for the turret to shoot at taking into account the bullet speed and object speed.

Here's an image showing the problem.

enter image description here

\$\endgroup\$
2
\$\begingroup\$

I've done something similar in the past and I used a quadratic equation to find the time t where the two paths (target and bullet) intersect.

This is what it looks like when implemented using libGDXs classes;

    private static Vector2 sub(Vector2 lhs, Vector2 rhs) {
        return (new Vector2(lhs)).sub(rhs);
    }

    private static Vector2 calculateFireDirection(Vector2 targetPosition, Vector2 turretPosition, Vector2 targetVelocity, float bulletSpeed) {
        Vector2 turretToTarget = sub(targetPosition, turretPosition);

        float a = targetVelocity.x*targetVelocity.x + targetVelocity.y * targetVelocity.y - bulletSpeed*bulletSpeed;
        float b = 2.0f * targetVelocity.dot(turretToTarget);

        float p = -b / (2 * a);
        float q = (float)Math.sqrt((b * b) - 4 * a * (turretToTarget.x * turretToTarget.y + turretToTarget.y * turretToTarget.y)) / (2 * a);

        float t = Math.max(p - q, p + q);

        if (t > 0) {
            Vector2 tp = new Vector2(targetPosition);
            Vector2 tv = new Vector2(targetVelocity);
            Vector2 collisionPoint = tp.add(tv);

            return sub(collisionPoint, turretPosition);
        }
        else
            return null; // This means the collision will never happen (or happened before the shot was fired, which is nonsensical).
    }    

I the formulas from somewhere, but I can't remember where.

A full program sample program;

public class Program {

    private static Vector2 sub(Vector2 lhs, Vector2 rhs) {
        return (new Vector2(lhs)).sub(rhs);
    }

    private static Vector2 calculateFireDirection(Vector2 targetPosition, Vector2 turretPosition, Vector2 targetVelocity, float bulletSpeed) {
        Vector2 turretToTarget = sub(targetPosition, turretPosition);

        float a = targetVelocity.x*targetVelocity.x + targetVelocity.y * targetVelocity.y - bulletSpeed*bulletSpeed;
        float b = 2.0f * targetVelocity.dot(turretToTarget);

        float p = -b / (2 * a);
        float q = (float)Math.sqrt((b * b) - 4 * a * (turretToTarget.x * turretToTarget.y + turretToTarget.y * turretToTarget.y)) / (2 * a);

        float t = Math.max(p - q, p + q);

        if (t > 0) {
            Vector2 tp = new Vector2(targetPosition);
            Vector2 tv = new Vector2(targetVelocity);
            Vector2 collisionPoint = tp.add(tv);

            return sub(collisionPoint, turretPosition);
        }
        else
            return null; // This means the collision will never happen (or happened before the shot was fired, which is non-sensical).
    }    

    private static void runSimulation(Vector2 targetPosition, Vector2 turretPosition, Vector2 targetVelocity, float bulletSpeed) {
        Vector2 direction = calculateFireDirection(targetPosition, turretPosition, targetVelocity, bulletSpeed);

        for(float t = 0; t < 10.0f; t += 0.1f) {
            Vector2 tsp = new Vector2(targetPosition);
            Vector2 tv = new Vector2(targetVelocity);

            Vector2 ssp = new Vector2(turretPosition);
            Vector2 sv = new Vector2(direction);


            Vector2 tp = tsp.add(tv.scl(t));
            Vector2 sp = ssp.add(sv.scl(t));


            float distance = sub(tp, sp).len();

            System.out.println(String.format("At time %.2f; Target=(%.2f, %.2f), Bullet=(%.2f, %.2f) distance=%.4f", t, tp.x, tp.y, sp.x, sp.y, distance));
            if (distance < 0.01f) {
                System.out.println("COLLISION!");
                break;
            }
        }
    }

    public static void main(String[] args) {

        // Input
        Vector2 targetPosition = new Vector2(1, 10);
        Vector2 targetVelocity = new Vector2(1, -1);

        Vector2 turretPosition = new Vector2(0, 0);
        float bulletSpeed = 10.0f;

        runSimulation(targetPosition, turretPosition, targetVelocity, bulletSpeed);
    }
}

Gives the output;

At time 0,00; Target=(1,00, 10,00), Bullet=(0,00, 0,00) distance=10,0499
At time 0,10; Target=(1,10, 9,90), Bullet=(0,20, 0,90) distance=9,0449
At time 0,20; Target=(1,20, 9,80), Bullet=(0,40, 1,80) distance=8,0399
At time 0,30; Target=(1,30, 9,70), Bullet=(0,60, 2,70) distance=7,0349
At time 0,40; Target=(1,40, 9,60), Bullet=(0,80, 3,60) distance=6,0299
At time 0,50; Target=(1,50, 9,50), Bullet=(1,00, 4,50) distance=5,0249
At time 0,60; Target=(1,60, 9,40), Bullet=(1,20, 5,40) distance=4,0199
At time 0,70; Target=(1,70, 9,30), Bullet=(1,40, 6,30) distance=3,0150
At time 0,80; Target=(1,80, 9,20), Bullet=(1,60, 7,20) distance=2,0100
At time 0,90; Target=(1,90, 9,10), Bullet=(1,80, 8,10) distance=1,0050
At time 1,00; Target=(2,00, 9,00), Bullet=(2,00, 9,00) distance=0,0000
COLLISION!
\$\endgroup\$
1
\$\begingroup\$

You're already part of the way there with recognising that your targets have a constant velocity - as well as the speed they are moving at, they're also moving in a specific direction (which is the difference between a speed and a velocity). If you know where the bullet is travelling (given its current location and velocity) you can calculate that after x time it'll be at a new location. Using a bit of basic maths with this, the position of the turret and the velocity of the bullets it fires, you can then calculate the position you want to be aiming at to hit after x time.

\$\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.