Unity 2D parabolic trajectory / projectile motion calculation

If you need to calculate the trajectory of a projectile launched by your character or by a NPC, or if you need to find the velocity you need to apply to your character to make him jump to a specific point in your 2D Unity game and you don't know anything about it, this article is for you!

The parabolic trajectory / the projectile motion

First we will look at the maths behind it. There are a couple of variables in theses equations. You can find more information in the Wikipedia article.

The intial variables

  •  \theta the launch angle at t=0
  • (Vox, Voy) the initial velocity of your projectile at t=0 which could be written (Vo * cos \theta , Vo * sin \theta ) with simple trigonometry.

Image from Wikipedia.

The kinematic variables

  • (x, y) the position of your projectile at given time
  • (Vx, Vy) the velocity of your projectile at a given time
  • (Ax, Ay) the acceleration of your projectile at a given time
  • t the time

Some interesting points : Ax is equal to zero because we are not accelerating in the horizontal axis. and Ay is equal to the gravity (-g).

Some interesting variables

  • h the maximum height of the projectile
  • d the maximum distance of the projectile

Ok that's a lot of variables, but how we are going to put this into Unity ?

Simplification

To simplify the problem we will choose three specific cases for our parabolic trajectory and apply them to our needs.

  1. The semi parabolic trajectory from the bottom left to the maximum height
  2. The semi parabolic trajectory from the maximum height to the bottom right
  3. The full parabolic trajectory going from the bottom left to the maximum height to the bottom right, starting and ending at the same height

We will use the full parabolic when we are at the same height of our target, the second from bottom to top when we are under our target and the last one from top to bottom when we are above our target.

Implementation in Unity

What do we already know ?

      
        // What we know
        float gravity = Physics2D.gravity.magnitude;
        float height = Mathf.Abs(target.y - origin.y);
        float dist = Mathf.Abs(target.x - origin.x);
  
  • h is the difference of height between you and the target and we can calculate it by substracting target height to our height
  • d is the distance between you and the target and we can calculate it by substracting target horizontal position to our position
  • -g the gravity is set in the Physics2D preferences

What do we need to calculate ?

     
        // What we want to find
        float vertVelocity = 0.0f;
        float time = 0.0f;
        float horzVelocity = 0.0f;
 
  • the initial velocity (Vox, Voy)
  • the time t

With the initial velocity we will be able to set the velocity manually to our rigidbody2D and let the Unity Physics system do the rest. There are other solutions of course but that's the one I chose.

So let's use the formula given in Wikipedia and apply it at different moments of our trajectory (ie: at the begining, at the maximum height and at the end of the trajectory).

Maths solutions

Remember (Vox, Voy) =  (Vo * cos \theta , Vo * sin \theta ).

First case: going up

 
        // If we are going upward
        // we will use a direct parabolic trajectory
        // and reach the highest point
        if (target.y - origin.y > 1.0f)
        {
            vertVelocity = Mathf.Sqrt(2.0f * gravity * height);
            time = vertVelocity / gravity;
            horzVelocity = dist / time;
        }
 

At the maximum height: we can calculate the vertical initial velocity with the formula for the maximum height found on Wikipedia because we know the maximum height for this case:

At the end of the trajectory: we need to calculate the time it should take with the following formula divided by 2 because we only do one half of the trajectory:

At the end of the trajectory: finally we can determine the horizontal initial velocity with:

Second case: going down

 
        // If we are going downward
        // we will use a direct parabolic trajectory
        // with no vertical velocity
        else if (target.y - origin.y < -1.0f)
        {
            vertVelocity = 0.0f;
            time = Mathf.Sqrt(2 * height / gravity);
            horzVelocity = dist / time;
        }
 

Same problem with verical velocity equals to zero! Yes we wont go up before going down because we are in our second simplified case.

We also need to change the time formula because Voy is equal to zero.

At the end of the trajectory: let's take the y formula and say y = -h because we want to know how long it will take to go from the top to -h with Voy equals to zero.

Third case: full parabolic trajectory

       
        // Else we will follow a full parabolic trajectory
        // and determine the height of the jump
        // depending on the distance between the 2 points
        else
        {
            height = dist / 4;
            vertVelocity = Mathf.Sqrt(2.0f * gravity * height);
            time = 2 * vertVelocity / gravity;
            horzVelocity = dist / time;
        }
 

This time we have one missing parameter we need to set: the height of the trajectory.

This is because we are at the same height of our target and so it will be zero and thus not a parabolic trajectory.
So let's set the height to a fourth of the distance, but we can choose what we want here.

The intial vertical velocity is still the same one we used in the first case. The time is the double because will do a full parabolic trajectory this time. And the horizontal initial velocity is the also the same.

Final code

After adding some basic checks we can wrap the code in a static function inside a helper class. And here we have a basic parabolic trajectory calculation. We only need to return a Vector2 composed of the intial (Vox, Voy) in the good direction and use it to throw stones, grenades or jump to a specific point.

Remember to activate the gravity of your rigidbody2D and, to prevent any disturbance in the force, remember to deactivate keyboard controls during your jump.

I added a vertical offset so I can adjust the trajectory or add some randomness into the NPC shots.

using UnityEngine;
using System.Collections;

//! Trajectory
/*!
 * Helper class to calculate trajectories.
 * Need origin point and target point.
 * Can specify a vertical offset.
 * Returns a Vector2.
 */
public class Trajectory
{
    //! Return initial velocity to reach a particular point in 2D
    public static Vector2 GetParableInitialVelocity(Vector3 origin, Vector3 target, float offsetY = 0.0f)
    {
        // Init trajectory variables
        float gravity = Physics2D.gravity.magnitude;
        float height = Mathf.Abs(target.y - origin.y + offsetY);
        float dist = Mathf.Abs(target.x - origin.x);
        float vertVelocity = 0.0f;
        float time = 0.0f;
        float horzVelocity = 0.0f;
        
        if (height < 0.1f) height = 0.1f; // Prevents division by zero
        if (gravity < 0.1f) gravity = 0.1f; // Prevents division by zero
        
        // If we are going upward
        // we will use a direct parable trajectory
        // and reach the highest point
        if (target.y - origin.y > 1.0f)
        {
            vertVelocity = Mathf.Sqrt(2.0f * gravity * height);
            time = vertVelocity / gravity;
            horzVelocity = dist / time;
        }
        // If we are going downward
        // we will use a direct parable trajectory
        // with no vertical velocity
        else if (target.y - origin.y < -1.0f)
        {
            vertVelocity = 0.0f;
            time = Mathf.Sqrt(2 * height / gravity);
            horzVelocity = dist / time;
        }
        // Else we will follow a full parable
        // and determine the height of the jump
        // depending on the distance between the 2 points
        else
        {
            height = dist / 4;
            vertVelocity = Mathf.Sqrt(2.0f * gravity * height);
            time = 2 * vertVelocity / gravity;
            horzVelocity = dist / time;
        }
        
        if (vertVelocity == 0.0f && horzVelocity == 0.0f)
        {
            return Vector2.zero;
        }
        
        // Jump right
        if(target.x - origin.x > 0.0f &&
           !float.IsNaN(vertVelocity) && !float.IsNaN(horzVelocity))
        {
            return new Vector2 (horzVelocity, vertVelocity);
        }
        // Jump left
        else if (!float.IsNaN(vertVelocity) && !float.IsNaN(horzVelocity))
        {
            return new Vector2 (-horzVelocity, vertVelocity);
        }
        else
        {
            return Vector2.zero;
        }
    }
}
 

I hope this basic projectile motion will help new Unity users to design nice physics based games.

Thanks to Wikipedia for the formula and pictures.

GitHub mark Download it on GitHub.

Menu