Projectile: initial velocity angle to hit coordinate.

Categories code, physic

While working on an aiming system for one of my games, I faced this problem: “What should be the initial velocity angle for a projectile to hit given coordinate?” I wanted the bullet to pass thru mouse position. This image illustrates the problem:

Projectile angle to hit coordinate

The projectile should hit coordinate marked with red cross. The launcher has fixed power and bullets have fixed mass, so in my scenario the initial velocity vector length will be constant (I will adjust it accordingly to get nice gameplay). The intuition says that I’ll end up with more than one trajectory. One will hit the coordinate after reaching maximum height and another will use the shortest parabolic path.

I tried to solve the problem by hand. It was clear that I need to start with basic trajectory equations for x and y movement and solve them for theta angle:
Trajecotry equationsThings quickly started to go wrong and even Wolfram wasn’t sure how to simplify. That was bad, but fortunately someone on the Wikipedia did remember the tricky trigonometric substitutions and wrote this:
Projectile euqtion solved

It says that the angle of the initial velocity vector depends on:

  • v – initial velocity (the length of velocity vector);
  • g – gravity value (given by positive number);
  • x, y – coordinates of the point that we want to hit;

Cool! First, It says that there are two solutions: one for adding square root and one for subtracting it (like I said, I’ll get two trajectories). Second, square root’s argument must be equal or greater than zero. Third, there’s x and g in the denominator, so both have to be non-zero. For now I just assume that square root argument has to be valid to make the projectile reach (x, y) coordinate. Gravity g must be non-zero to even think of ballistic behavior. If the x is zero, then the angle is straightforward: π/2 or -π/2 depending on y sign.

I did small Flash application for testing this equation:

projectile

This demo uses Nape physic engine and works with Flash coordinates system, where (0, 0) means top-left corner of the screen. But ProjectileUtils class is designed for “human” coordinate system, where [0, 10] is the velocity for up movement. Pass the gravity as a positive number. Feel free to grab the source code from GitHub Projectile-Utils repository or just take a look at ProjectileUtils class:

package {

    /**
     * Projectile trajectory utils.
     * @author maciek grzybowski, 10.11.13 14:29
     *
     */
    public class ProjectileUtils {

        /**
         * Checks if projectile can hit (x, y) coordinate with initial velocity length under given gravity.
         * @param x
         * @param y
         * @param velocity initial velocity
         * @param gravity gravity value; should be greater than 0
         * @return
         */
        public static function canHitCoordinate(x:Number, y:Number, velocity:Number, gravity:Number):Boolean {
            return calculateDelta(x, y, velocity, gravity) >= 0;
        }

        /**
         * Calculates angle to hit given (x, y) coordinate with given velocity and gravity.
         * @param x
         * @param y
         * @param velocity initial velocity
         * @param gravity gravity value; should be greater than 0
         * @return angle in radians
         */
        public static function calculateAngle1ToHitCoordinate(x:Number, y:Number, velocity:Number, gravity:Number):Number {
            if (x == 0) return y > 0 ? -Math.PI * 0.5 : Math.PI * 0.5;
            var delta:Number = calculateDelta(x, y, velocity, gravity);
            var sqrtDelta:Number = Math.sqrt(delta);
            return Math.atan((velocity * velocity - sqrtDelta)/(gravity * x));;
        }

        /**
         * Calculates angle to hit given (x, y) coordinate with given velocity and gravity.
         * @param x
         * @param y
         * @param velocity initial velocity
         * @param gravity gravity value; should be greater than 0
         * @return angle in radians
         */
        public static function calculateAngle2ToHitCoordinate(x:Number, y:Number, velocity:Number, gravity:Number):Number {
            if (x == 0) return -Math.PI * 0.5;
            var delta:Number = calculateDelta(x, y, velocity, gravity);
            var sqrtDelta:Number = Math.sqrt(delta);
            return Math.atan((velocity * velocity + sqrtDelta)/(gravity * x));
        }

        private static function calculateDelta(x:Number, y:Number, velocity:Number, gravity:Number):Number {
            return velocity * velocity * velocity * velocity - gravity * (gravity * x * x + 2 * y * velocity * velocity)
        }
    }
}

2 Comments

  • Rw Liebenberg
    01/04/2014

    Hi M

    Great Utility, very cool. Wanted to know how can you work the target objects’s MASS and Gravitational Mass into the equations? And also possible add the WORLD linear Drag of the nape space?

  • Maciek
    15/04/2014

    Equation that I described solves the problem for velocity so there is nothing to say about the mass. In other words, if you set the initial velocity as I described, it will always hit the coordinate (assuming that there’s no air resistance) – no matter what the mass of projectile is. This is what Galileo proved ;).

    However, if you want to think in terms of mass, you should probably start with Newton’s 2nd law of motion and calculate the initial impulse (force acting over very short period of time) with your custom gravity. But, frankly speaking, this will be a bit complicated. What’s wrong with using just a velocity? As long as you are not doing any realistic simulation (e.g. with an air resistance) I think you don’t need to bother about the mass. Otherwise you should use more sophisticated methods for such calculations, because Euler integration implemented in my code is a source of big inacurracy.

Comments are closed.