Search Unity

projectile trajectory prediction

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

  1. Adam_Benko

    Adam_Benko

    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

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    You have all the needed information in this page : https://en.m.wikipedia.org/wiki/Projectile_motion


    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.     }
     
  3. Adam_Benko

    Adam_Benko

    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

    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.
  5. Adam_Benko

    Adam_Benko

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

    Attached Files:

  6. SparrowGS

    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.
  7. Adam_Benko

    Adam_Benko

    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 :)
     
  8. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
     
  9. Split3

    Split3

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

    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

    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

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,314
    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

    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

    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

    gabrielkazansky

    Joined:
    Sep 27, 2020
    Posts:
    1

    THX bro this really helped me out,Code is superb,and works.
     
    Split3 likes this.
  16. jadvrodrigues

    jadvrodrigues

    Joined:
    Feb 25, 2020
    Posts:
    12
    As seen here, you can easily shoot using both the bullet and the target's position/velocity/acceleration into consideration.
     
    IMC-Lab likes this.