# projectile trajectory prediction

Discussion in 'Physics' started by Adam_Benko, Apr 20, 2019.

Joined:
Jun 16, 2018
Posts:
105
Hi. I have AI autoturret. Everything works fine (prediction for shooting, detecting enemies). However, I have a trouble with implementation of a bullet drop (gravity for a bullet). I have seen many posts about ballistic bullets and it's trajectory but I cant make it work.
What my turret needs is a Vector 3 target for shooting. Right now, it has its computed vector3 target. I need to access the Vector3.y value of the target and increase it based on the distance from the target, gravity applied to the bullet and the position of the target and make a new Vector 3. (speed of the bullet is constant)
Can anybody help me with this ? Thanks

2. ### SparrowGS

Joined:
Apr 6, 2017
Posts:
2,536

Feel free to use my own class if you dont want to write your own.
Code (CSharp):
1. using UnityEngine;
2. using System;
3.
4.     public static class Ballistics {
5.
6.         // Note, doesn't take drag into account.
7.
8.         /// <summary>
9.         /// Calculate the lanch angle.
10.         /// </summary>
11.         /// <returns>Angle to be fired on.</returns>
12.         /// <param name="start">The muzzle.</param>
13.         /// <param name="end">Wanted hit point.</param>
14.         /// <param name="muzzleVelocity">Muzzle velocity.</param>
15.         public static bool CalculateTrajectory(Vector3 start, Vector3 end, float muzzleVelocity, out float angle){//, out float highAngle){
16.
17.             Vector3 dir = end - start;
18.             float vSqr = muzzleVelocity * muzzleVelocity;
19.             float y = dir.y;
20.             dir.y = 0.0f;
21.             float x = dir.sqrMagnitude;
22.             float g = -Physics.gravity.y;
23.
24.             float uRoot = vSqr * vSqr - g * (g * (x) + (2.0f * y * vSqr));
25.
26.
27.             if (uRoot < 0.0f) {
28.
29.                 //target out of range.
30.                 angle = -45.0f;
31.                 //highAngle = -45.0f;
32.                 return false;
33.             }
34.
35.     //        float r = Mathf.Sqrt (uRoot);
36.     //        float bottom = g * Mathf.Sqrt (x);
37.
38.             angle = -Mathf.Atan2 (g * Mathf.Sqrt (x), vSqr + Mathf.Sqrt (uRoot)) * Mathf.Rad2Deg;
39.             //highAngle = -Mathf.Atan2 (bottom, vSqr - r) * Mathf.Rad2Deg;
40.             return true;
41.
42.         }
43.
44.         /// <summary>
45.         /// Gets the ballistic path.
46.         /// </summary>
47.         /// <returns>The ballistic path.</returns>
48.         /// <param name="startPos">Start position.</param>
49.         /// <param name="forward">Forward direction.</param>
50.         /// <param name="velocity">Velocity.</param>
51.         /// <param name="timeResolution">Time from frame to frame.</param>
52.         /// <param name="maxTime">Max time to simulate, will be clamped to reach height 0 (aprox.).</param>
53.
54.         public static Vector3[] GetBallisticPath(Vector3 startPos, Vector3 forward, float velocity, float timeResolution, float maxTime = Mathf.Infinity){
55.
56.             maxTime = Mathf.Min (maxTime, Ballistics.GetTimeOfFlight (velocity, Vector3.Angle (forward, Vector3.up) * Mathf.Deg2Rad, startPos.y));
57.             Vector3[] positions = new Vector3[Mathf.CeilToInt(maxTime / timeResolution)];
58.             Vector3 velVector = forward * velocity;
59.             int index = 0;
60.             Vector3 curPosition = startPos;
61.             for (float t = 0.0f; t < maxTime; t += timeResolution) {
62.
63.                 if (index >= positions.Length)
64.                     break;//rounding error using certain values for maxTime and timeResolution
65.
66.                 positions [index] = curPosition;
67.                 curPosition += velVector * timeResolution;
68.                 velVector += Physics.gravity * timeResolution;
69.                 index++;
70.             }
71.             return positions;
72.         }
73.
74.         /// <summary>
75.         /// Checks the ballistic path for collisions.
76.         /// </summary>
77.         /// <returns><c>false</c>, if ballistic path was blocked by an object on the Layermask, <c>true</c> otherwise.</returns>
78.         /// <param name="arc">Arc.</param>
79.         /// <param name="lm">Anything in this layer will block the path.</param>
80.         public static bool CheckBallisticPath(Vector3[] arc, LayerMask lm){
81.
82.             RaycastHit hit;
83.             for (int i = 1; i < arc.Length; i++) {
84.
85.                 if (Physics.Raycast (arc [i - 1], arc [i] - arc [i - 1], out hit, (arc [i] - arc [i - 1]).magnitude) && GameMaster.IsInLayerMask(hit.transform.gameObject.layer, lm))
86.                     return false;
87.
88.     //            if (Physics.Raycast (arc [i - 1], arc [i] - arc [i - 1], out hit, (arc [i] - arc [i - 1]).magnitude) && GameMaster.IsInLayerMask(hit.transform.gameObject.layer, lm)) {
89.     //                Debug.DrawRay (arc [i - 1], arc [i] - arc [i - 1], Color.red, 10f);
90.     //                return false;
91.     //            } else {
92.     //                Debug.DrawRay (arc [i - 1], arc [i] - arc [i - 1], Color.green, 10f);
93.     //            }
94.             }
95.             return true;
96.         }
97.
98.         public static Vector3 GetHitPosition(Vector3 startPos, Vector3 forward, float velocity){
99.
100.             Vector3[] path = GetBallisticPath (startPos, forward, velocity, .35f);
101.             RaycastHit hit;
102.             for (int i = 1; i < path.Length; i++) {
103.
104.                 //Debug.DrawRay (path [i - 1], path [i] - path [i - 1], Color.red, 10f);
105.                 if (Physics.Raycast (path [i - 1], path [i] - path [i - 1], out hit, (path [i] - path [i - 1]).magnitude)) {
106.                     return hit.point;
107.                 }
108.             }
109.
110.             return Vector3.zero;
111.         }
112.
113.
114.         public static float CalculateMaxRange(float muzzleVelocity){
115.             return (muzzleVelocity * muzzleVelocity) / -Physics.gravity.y;
116.         }
117.
118.         public static float GetTimeOfFlight(float vel, float angle, float height){
119.
120.             return (2.0f * vel * Mathf.Sin (angle)) / -Physics.gravity.y;
121.         }
122.
123.     }

Joined:
Jun 16, 2018
Posts:
105
Thank you very much for the reply.
I am not sure if I am asking too much but, could you please tell, how would you modify your script, if you wanted only the direction (vector3) to be calculated, instead of the Angle to be fired on ?
I mean, my turret script needs vector3 target. So If I could give it only the direction for shooting instead of the angle, it would be ideal

gabrielkazansky likes this.
4. ### SparrowGS

Joined:
Apr 6, 2017
Posts:
2,536
I'm not sure what you mean by that, do you mean what would be the forward vector of given angle?

you would have to calculate it with the function I gave you earlier, this is a snippet from my game, it's use to aim a mortar/howitzer.

Code (CSharp):
1.         public override Quaternion AimRotation(Vector3 start, Vector3 end, Vector3 velocity){
2.
3.             float low;
4.             //float high;
5.             Ballistics.CalculateTrajectory (start, end, velocity, out low);//, out high); //get the angle
6.
7.
8.             Vector3 wantedRotationVector = Quaternion.LookRotation (end - start).eulerAngles; //get the direction
9.             wantedRotationVector.x = low; //combine the two
10.             return Quaternion.Euler (wantedRotationVector); //into a quaternion
11.         }
12.
btw, you can see all those "high" angles commented out, this is for performance because I don't need it right now, you can uncomment it back it and use it if you want the high tarjectory.

Erfan1386 and anycolourulike like this.

Joined:
Jun 16, 2018
Posts:
105
This is what I need mate.

File size:
69.2 KB
Views:
2,082
6. ### SparrowGS

Joined:
Apr 6, 2017
Posts:
2,536
I literally gave you exactly this.

Call 'CalculateTrajectory' with the muzzle position for the 'start' variable, the target position for the 'end' variable, and the speed at launch, IE the muzzle velocity for the 'velocity' variable, this will output the angle into 'low' (you can also uncomment 'high' incase you want that).

this will angle the turret to look along the hard line you've drawn, but when you fire a rigidbody projectile along the vector gravity will pull it along the dotted line.

if you want a vector 3 array with the arch given gravity you can call 'GetBallisticPath'.

Erfan1386 likes this.

Joined:
Jun 16, 2018
Posts:
105
Ok, so lets say that I got the target and I rotate the turret with newRotation (Quaternion).
Now, I have a low float from you, that gives me the extra angle, to compensate for the bullet drop.
How do I modify the already existing newRotation, that points at the target, with a "low" float from your script ? So that I have modified , newRotationWithLow (Quaternion) ?
Thanks

Joined:
Apr 6, 2017
Posts:
2,536

9. ### Split3

Joined:
Aug 21, 2017
Posts:
17
Here's a full tutorial:

10. ### K0ST4S

Joined:
Feb 2, 2017
Posts:
35
Hello. How would you follow the ballistic path with a gameobject, so that the GetTimeOfFlight method value would be accurate?

11. ### cfinger

Joined:
Feb 29, 2020
Posts:
3
For anyone referencing this from google, I had to make one change. Most likely because my start and end locations are at different heights (y).

Code (CSharp):
1. wantedRotationVector.x += low; //combine the two
@SparrowGS thank you for posting this- I had fun trying to derive the equations until I got into trig substitutions and went to google instead

Ruslank100 and SparrowGS like this.
12. ### Qriva

Joined:
Jun 30, 2019
Posts:
1,306
I was a step away from losing sanity.

@SparrowGS your CalculateTrajectory is not valid, you mixed +- and order in atan2 and function produces invalid results.
This function takes into account different heights, you probably encountered the same problem, but you false "fixed it" by combining some angles.

This is correct:
Code (CSharp):
1. angle = -(Mathf.Atan2 (vSqr - r, bottom) * Mathf.Rad2Deg);
2. highAngle = -(Mathf.Atan2 (vSqr + r, bottom) * Mathf.Rad2Deg);

13. ### Erfan1386

Joined:
Feb 3, 2021
Posts:
5
Hi and thanks for your code. This math calculations are really REALLY hard for someone like me from eights grade

14. ### Sackstand

Joined:
May 26, 2015
Posts:
10
hi there i think the the method "GetBallisticPath" the Angle calc should be forward instead of up
Code (CSharp):
1.  Vector3.Angle(forward, Vector3.forward)
also the time calculation should more look like this:

Code (CSharp):
1.     public static float GetTimeOfFlight(float vel, float angle, float height)
2.     {
3.         var a = vel * Mathf.Sin(angle);
4.         return (a + Mathf.Sqrt(Mathf.Pow(a, 2) + 2 * -Physics.gravity.y * height)) / -Physics.gravity.y;
5.     }
where height is the base height from the launching point and ground is asumed as 0

PS: not performance optimized yet

15. ### gabrielkazansky

Joined:
Sep 27, 2020
Posts:
1

THX bro this really helped me out,Code is superb,and works.

Split3 likes this.