It’s been a while 🙂 I have been busy and did not have the energy to finish this 4 part series of projectile calculations. Now I am finally putting out the last bit. Calculating the projectile movement in 3D space using your own movement code.
For starters, lets refresh what I wanted to write in the first place. I was working with a project where I needed to calculate the a projectile path in 3D space using my own movement code rather than relying on the rigid body and physic engine. In my case I absolutely wanted the projectile to hit the target always so I couldn’t go with the calculate path with any given velocity and any given angle.
For a refreshment’s sake, let’s revisit this picture. Red diamond is the source and green cube is the target. 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. Y axis is up and Z is forward.
So last time we used rigid body and it’s AddForce method to do all the work. Now we need to calculate the position by ourselves. So lets start 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 interested in horizontal velocity having 2 components (X and Z) and vertical component Y we need also calculate direction because we want to manipulate the X and Z correctly. Direction we can get by subtracting the start and end positions and taking the normalized of resulting vector. So: Vector3 direction = (end – start).normalized;
You are also going to need the duration the projectile will stay in the air. Well, you could also not care about this but probably you want to do some cleanup code so your scene will not get filled with projectiles that are already landed and immobile. You can use the horizontal distance as base for duration or you can use the Y component thus velocity and gravity. Since we used the horizontal distance last time let’s now use the Y component. So the duration can be calculated by: (Sin(Angle) * velocity * 2) / gravity.
So in our example: d = 21.2132, a = 66.0, g = 9.81, replacing the variables in the velocity equation give us v = 16.74. Duration is: 3.11
If you remember from the 2D example we used the Unity’s update() function to move the projectile. This is perfect fit again since we have just one more component it’s basically the same.
So we are going to utilize the Time.deltaTime so we know how long as time passed since last update and multiply that with horizontal velocity. This is going to be our fraction of the journey up to the destination in horizontal space. Then we need to multiply the (horizontal) speed with the fraction multiplied by direction. So code is:
var horizontal = Mathf.Cos(Angle * Mathf.Deg2Rad) * velocity;
float frac = (Time.deltaTime * horizontal / distance);
var direction = (target.transform.position - start).normalized;
float x = frac * distance * direction.x;
float z = frac * distance * direction.z;
I am actually doing unnecessarily the direction inside the Update() since start and end do not change during the projectile flight. Anyways. This is the increment / decrement you need to apply to X and Z components.
What about the Y component? This is exactly the same as previously. From the wikipedia article, height at arbitrary distance X: By feeding our parameters in the formula: height y = start.y + x * tan(angle) – (gravity * pow(x) / 2 * pow((velocity * cos(angle)))). Note: x here is the distance from the start. Well, wikipedia article is 2D so X is the distance from the start but we need to use both X and Z components. Well we actually have all we need, we did the fraction right?
Vector2 position = new Vector2(transform.position.x + x, transform.position.z + z);
This is the current projectile position and since I am using the code in the projectile, transform refers to this GameObject’s transform. If we take the position.magnitude we have the distance.
Ultimately we want to do this:
this.transform.position = new Vector3(position.x, y, position.y);
Note: We projected the X,Z movement to Vector2 so it do not have Z we need to use Y. You can do this also without the Vector2 by calculating the distance by:
sqrt(pow(start.x – end.x) + pow(start.z – end.z)).
So here it is, now you can do the projectile calculation in 3D by using your own code. The complete solutions is not here, but all the relevant bits are here so you should be able to complete the Update() function very easily.
What do you think of all this? Do you have any suggestions or feedback. Please leave a comment and happy coding!