Projectile calculations in 3D space, using rigid bodies

If you have been following what I wanted to write regarding projectile calculations you know now how to do the calculations in 2D with using both rigid bodies and by calculating the movement code yourself. Now we add one dimension and see how it effects things.

Let’s take a look at this picture. This is pretty similar than the previous one but with one more axis involved. This is your game where you have source (red diamond) and target (green cube). Source position is x1, y1, z1 and target position is x2, y2, z2. The angle between x -axis and projectile is theta which is 66 degrees in the example. Notice how now Y axis is up and Z is forward.


We are still going to need the velocity vector like previously which then can be applied to rigid body. You have now three components X, Y and Z that you need to solve because you want the projectile to move to all directions.

With the same formula based on Wikipedia you can solve the velocity from distance formula when both start and end y coordinates are 0. v = 1 / cos(a) * sqrt((0.5 * g * d * d) / (d * tan(a))); where v = velocity, d = distance, g = gravity and a = angle.

Since we are now interested in horizontal velocity having 2 components (X and Z) and vertical component Y we need also calculate direction because that is going to dictate how much force we need per component. Direction we can get by subtracting the start and end positions and taking the normalized of resulting vector. So: Vector3 direction = (end – start).normalized;

Distance you get by d = sqrt(pow((x2 – x1)) + pow(y2 – y1)), or Vector3.Distance(start, end) or (start,end).magnitude (where start and end are Vector3 objects)

So in our example: d = 21.2132, a = 66.0, g = 9.81, replacing the variables in the equation give us v = 15.0929.

Remember how we used the rigid body’s AddForce -method to apply the velocity vector? We can use the same function but now we are going to need the Z -component too. Last time we did this:

var distance = Vector3.Distance(target.transform.position, position);
var velocity = (float)(1 / Mathf.Cos(angle * Mathf.Deg2Rad)) * Mathf.Sqrt((float)(0.5f * 9.81 * distance * distance) / (distance * Mathf.Tan(angle * Mathf.Deg2Rad)));
float horizontal = Mathf.Cos(angle * Mathf.Deg2Rad) * velocity;
var direction = (target.transform.position - position).normalized;
var rbVelocity = new Vector2(direction.x * horizontal, Mathf.Sin(angle * Mathf.Deg2Rad) * velocity);
rigidBody.AddForce(rbVelocity, ForceMode.Impulse);

Now we just use Vector3 like this:

var rbVelocity = new Vector3(direction.x * horizontal, Mathf.Sin(angle * Mathf.Deg2Rad) * velocity, direction.z * horizontal);

That is basically all we need. Adding the third dimension was very easy since the direction already contains both X and Z components that we need. Now you can fire a projectile that will always land on it target so matter what is used angle (with the exception of 90 and 0 of course). What do you think, any ideas or suggestions? Please, leave a comment.