Search Unity

  1. Get the latest news, tutorials and offers directly to your inbox with our newsletters. Sign up now.
    Dismiss Notice

Projectile trajectory accounting for gravity/velocity/mass/distance?

Discussion in 'Physics' started by Ninekorn, Aug 12, 2016.

  1. Ninekorn

    Ninekorn

    Joined:
    Aug 12, 2016
    Posts:
    65
    Hi,
    It's been 3 days since i've started trying to write a script that makes my tanks shoot projectiles to their targets. But it's not working. It's a 3d simulation tank game (more a testing bs game for the moment hehe). I have searched the internet and the Unity forums for a solution and all i've come up with are the following solution threads:

    1.http://answers.unity3d.com/questions/45271/calculating-a-projectiles-angle-required-to-hit-ob.html
    2.http://answers.unity3d.com/questions/49195/trajectory-of-a-projectile-formula-does-anyone-kno.html
    3.https://en.wikipedia.org/wiki/Trajectory_of_a_projectile
    4.http://forum.unity3d.com/threads/ca...o-hit-target-position-angle-always-90.373197/
    5.http://gamedev.stackexchange.com/questions/80752/projectile-motion-based-off-angle

    I have tried lowering the gravity to 0 and lowering mass of projectile to 0.00000001 but still the angle is always off.
    Here is the script I use so far for the gun:
    Vector3 cannon= gun.position;
    Vector3 enemy = target.position;
    Vector3 gun1 = cannon.position;

    var x = target.position.x - cannon.position.x;
    var xz = target.position.z - cannon.position.z;
    //var xx = target.position.x - cannon.position.x;
    //var x = Mathf.Sqrt(xx * xx + xz * xz);

    var y = enemy.y - gun1.y;
    //y = 0; <----when I turn off gravity on the projectile I use this----
    var v = 500f; //m/s
    var g = Physics.gravity.y; //m/s

    var sqrt = (v * v * v * v) - (g * (g * (x * x) + 2 * y * (v * v)));

    sqrt = Mathf.Sqrt(sqrt);

    var angleInRadians = Mathf.Atan(((v * v) + sqrt) / (g * x));
    var angleInRadians1 = Mathf.Atan(((v * v) - sqrt) / (g * x));

    var angleInDegrees = angleInRadians * Mathf.Rad2Deg;
    var angleInDegrees1 = angleInRadians1 * Mathf.Rad2Deg;


    Vector3 gunrotation = enemy - gun1;

    gunrotation.y += angleInDegrees;
    gun.rotation = Quaternion.Slerp(gun.rotation, Quaternion.LookRotation(gunrotation), 1f);



    The projectile script is basic where velocity of 500 is added to the rigidbody in the Vector3.forward direction. Can anybody help me. I am no mathematics savvy and no physics savvy either. Btw if I turn Gravity off on the projectile than it ALWAYS hits the target. But without gravity the Script could be as simple as getting the Hypothenuse (target.position.x-transform.position.x for side 1 then side 2 target.position.y - transform.position.y)and shooting in that direction just like in this snippet of code:

    float OppositeSide = target.position.y - cannon.position.y;
    float AdjacentSide = target.position.x - cannon.position.x;
    float division = OppositeSide/ AdjacentSide;

    float angleRad = Mathf.Tan(division);
    float angleDeg = angleRad * Mathf.Rad2Deg;
     
    Last edited: Aug 12, 2016
  2. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    776
    Hey Ninecorn, I don't see on your list a thread I contributed to, so I'd recommend checking it out. As well, you should also wrap you code using the [ code ] tags. This formats the code to be monospace and makes it look similar to how you'd see in Notepad or an IDE.
     
  3. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,615
    There are two calculations required to hit a moving target.

    1. FirstOrderIntercept. This is a calculation of how far ahead of the target you need to aim based on speed, projectile speed, and distance.

    Code (csharp):
    1.  
    2.  
    3.     //first-order intercept using absolute target position
    4.     public static Vector3 FirstOrderIntercept
    5.     (
    6.         Vector3 shooterPosition,
    7.         Vector3 shooterVelocity,
    8.         float shotSpeed,
    9.         Vector3 targetPosition,
    10.         Vector3 targetVelocity
    11.     )
    12.     {
    13.         Vector3 targetRelativePosition = targetPosition - shooterPosition;
    14.         Vector3 targetRelativeVelocity = targetVelocity - shooterVelocity;
    15.         float t = FirstOrderInterceptTime
    16.         (
    17.             shotSpeed,
    18.             targetRelativePosition,
    19.             targetRelativeVelocity
    20.         );
    21.         return targetPosition + t * (targetRelativeVelocity);
    22.     }
    23.     //first-order intercept using relative target position
    24.     public static float FirstOrderInterceptTime
    25.     (
    26.         float shotSpeed,
    27.         Vector3 targetRelativePosition,
    28.         Vector3 targetRelativeVelocity
    29.     )
    30.     {
    31.         float velocitySquared = targetRelativeVelocity.sqrMagnitude;
    32.         if (velocitySquared < 0.001f)
    33.             return 0f;
    34.  
    35.         float a = velocitySquared - shotSpeed * shotSpeed;
    36.  
    37.         //handle similar velocities
    38.         if (Mathf.Abs(a) < 0.001f)
    39.         {
    40.             float t = -targetRelativePosition.sqrMagnitude /
    41.             (
    42.                 2f * Vector3.Dot
    43.                 (
    44.                     targetRelativeVelocity,
    45.                     targetRelativePosition
    46.                 )
    47.             );
    48.             return Mathf.Max(t, 0f); //don't shoot back in time
    49.         }
    50.  
    51.         float b = 2f * Vector3.Dot(targetRelativeVelocity, targetRelativePosition);
    52.         float c = targetRelativePosition.sqrMagnitude;
    53.         float determinant = b * b - 4f * a * c;
    54.  
    55.         if (determinant > 0f)
    56.         { //determinant > 0; two intercept paths (most common)
    57.             float t1 = (-b + Mathf.Sqrt(determinant)) / (2f * a),
    58.                     t2 = (-b - Mathf.Sqrt(determinant)) / (2f * a);
    59.             if (t1 > 0f)
    60.             {
    61.                 if (t2 > 0f)
    62.                     return Mathf.Min(t1, t2); //both are positive
    63.                 else
    64.                     return t1; //only t1 is positive
    65.             }
    66.             else
    67.                 return Mathf.Max(t2, 0f); //don't shoot back in time
    68.         }
    69.         else if (determinant < 0f) //determinant < 0; no intercept path
    70.             return 0f;
    71.         else //determinant = 0; one intercept path, pretty much never happens
    72.             return Mathf.Max(-b / (2f * a), 0f); //don't shoot back in time
    73.     }
    74.  
    75.  
    The second calculation is determine for bullet drop

    Code (csharp):
    1.  
    2.  
    3.     public static bool CalculateTrajectory(float TargetDistance, float ProjectileVelocity, out float CalculatedAngle)
    4.     {
    5.         CalculatedAngle = 0.5f * (Mathf.Asin ((-Physics.gravity.y * TargetDistance) / (ProjectileVelocity * ProjectileVelocity)) * Mathf.Rad2Deg);
    6.         if(float.IsNaN(CalculatedAngle))
    7.         {
    8.             CalculatedAngle = 0;
    9.             return false;
    10.         }  
    11.         return true;
    12.     }
    13.  
    14.  
    usage...

    Code (csharp):
    1.  
    2.  
    3. Transform Target;
    4.  
    5. void Fire()
    6. {
    7.  
    8. float projectileVelocity = 150;
    9. float distance = Vector3.Distance(transform.position, Target.position);
    10. float trajectoryAngle;
    11.  
    12. Vector3 TargetCenter = FirstOrderIntercept(transform.position, Vector3.zero, projectileVelocity, Target.position, Target.rigidbody.velocity);
    13.  
    14.  
    15. if(CalculateTrajectory(distance, projectileVelocity, out trajectoryAngle))
    16. {
    17.           float trajectoryHeight = Mathf.Tan(trajectoryAngle * Mathf.Deg2Rad) * Distance;
    18.           TargetCenter.y += trajectoryHeight;
    19. }
    20.  
    21. //fire at TargetCenter
    22.  
    23. }
    24.  
    25.  
     
  4. Ninekorn

    Ninekorn

    Joined:
    Aug 12, 2016
    Posts:
    65
    Hi Iron-Warrior and JamesLeeNZ. Thank you for your fast responses. I'm sorry It took a long time for me to reply back.

    Iron: I checked your thread and unfortunately I couldn't get the script working. It gives me an error of ''couldn't apply velocity to Rigidbody since it is (NaN,NaN,NaN). I must be inserting the wrong parameters or something. I'm quite newbie to the programming world. I'm going to try it again real soon.

    James: Right before you posted your message I went on to buy an Asset from the Store that calculates the projectiles angles called "Ballistic Projectile Helper 1.01" for 5$. It's working as I expected. There is two projectiles direction possibilities using his script and the first one is with Parabolic Arc descending on the target or a direct shot hitting the target with the less angle possible. Both used my input on the projectiles velocity. But I feel I am going to need your scripts and so I'm gonna let this thread sleep a while before I come back to it.

    I'm sorry if you feel I wasted your time but I just got pushed back a few days of work after deciding to install Unity 5.4.0f3 stable... I used to run the Beta 5.4.0b24. Now my tank treads don't roll properly so I've gotta restart the tanks setup all over again lol. but at least I got your stuff and the Projectile Helper script. Once I get back on track i am coming back to this thread.
     
    Last edited: Aug 14, 2016
  5. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,615
    Yes there are two variations of trajectory

    High Arc (lob).. that's the one you tried to implement. The formula is a whole bunch harder to implement but possible. I have a working version. I dont usually bother with that one, but It has its uses. Artillery strike for example.

    Low Arc (line of sight). That's the one I use mostly. More of a directly at target with enough extra height to make sure it doesnt hit dirt before the target. In the wiki, its listed as Angle of Reach.

    Hopefully the pack you brought also includes the first order intercept stuff, otherwise youll never hit a moving target.

    No wasted effort if it helps someone in the future.
     
  6. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    776
    When you apply the script, set the initial angle variable to something like 45 degrees. Then, set the target variable to any other object in the world. Make sure the object that has the script attached has a rigidbody. I just tested it, so let me know if it doesn't work!

    [/quote]I'm sorry if you feel I wasted your time but I just got pushed back a few days of work after deciding to install Unity 5.4.0f3 stable... I used to run the Beta 5.4.0b24. Now my tank treads don't roll properly so I've gotta restart the tanks setup all over again lol. but at least I got your stuff and the Projectile Helper script. Once I get back on track i am coming back to this thread.[/QUOTE]

    Like James said above, don't worry about wasting time on the forum, since it's pretty likely other people will have the same or similar problems. The forums aren't just for discussion, but also function as a archive of useful information.
     
unityunity