Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Throw an object along a parabola?

Discussion in 'Editor & General Support' started by completelykate, Nov 16, 2012.

Thread Status:
Not open for further replies.
  1. completelykate

    completelykate

    Joined:
    Oct 19, 2012
    Posts:
    4
    Hey there,

    I'm trying to get my main character to be able to throw objects. Currently I'm just applying a force to the object, which works, but is wildly unpredictable if the player is trying to throw at a very specific point.

    What I would LIKE to do is for the thrown object to always follow the same parabola from the player, so the player can better gauge when to throw an object. Something like the throwing seen here in Ocarina of Time:

    https://www.youtube.com/watch?v=gc8xX5pkFu4#t=14m55s

    I COULD transform the thrown object along a curve, but that would ignore obstacles. How can I best implement throwing this way?
     
  2. Stephan-B

    Stephan-B

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    There are several ways to achieve this.

    1) You could use a cubic spline to define the curve of the throw. The first point would be the location of the player, 2nd (control point) the height of the throw and last point the target. To learn more about this, check out this website http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/

    2) You could use projectile motion to calculate how much force is needed to apply force to a Rigidbody to throw it a certain distance at a certain angle to reach a target.

    3) You could still use projectile motion but simulate gravity and bypass physics to have better control over projectile speed and simulation. Below is sample code for this. Here is a short video showing the results of this script http://www.youtube.com/watch?v=3B5NV4bAkoE

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class ThrowSimulation : MonoBehaviour
    5. {
    6.     public Transform Target;
    7.     public float firingAngle = 45.0f;
    8.     public float gravity = 9.8f;
    9.  
    10.     public Transform Projectile;      
    11.     private Transform myTransform;
    12.    
    13.     void Awake()
    14.     {
    15.         myTransform = transform;      
    16.     }
    17.  
    18.     void Start()
    19.     {          
    20.         StartCoroutine(SimulateProjectile());
    21.     }
    22.  
    23.  
    24.     IEnumerator SimulateProjectile()
    25.     {
    26.         // Short delay added before Projectile is thrown
    27.         yield return new WaitForSeconds(1.5f);
    28.        
    29.         // Move projectile to the position of throwing object + add some offset if needed.
    30.         Projectile.position = myTransform.position + new Vector3(0, 0.0f, 0);
    31.        
    32.         // Calculate distance to target
    33.         float target_Distance = Vector3.Distance(Projectile.position, Target.position);
    34.  
    35.         // Calculate the velocity needed to throw the object to the target at specified angle.
    36.         float projectile_Velocity = target_Distance / (Mathf.Sin(2 * firingAngle * Mathf.Deg2Rad) / gravity);
    37.  
    38.         // Extract the X  Y componenent of the velocity
    39.         float Vx = Mathf.Sqrt(projectile_Velocity) * Mathf.Cos(firingAngle * Mathf.Deg2Rad);
    40.         float Vy = Mathf.Sqrt(projectile_Velocity) * Mathf.Sin(firingAngle * Mathf.Deg2Rad);
    41.  
    42.         // Calculate flight time.
    43.         float flightDuration = target_Distance / Vx;
    44.    
    45.         // Rotate projectile to face the target.
    46.         Projectile.rotation = Quaternion.LookRotation(Target.position - Projectile.position);
    47.        
    48.         float elapse_time = 0;
    49.  
    50.         while (elapse_time < flightDuration)
    51.         {
    52.             Projectile.Translate(0, (Vy - (gravity * elapse_time)) * Time.deltaTime, Vx * Time.deltaTime);
    53.            
    54.             elapse_time += Time.deltaTime;
    55.  
    56.             yield return null;
    57.         }
    58.     }  
    59. }
    60.  
    P.S. Tweaking gravity allows you to fire the projectile at a much higher velocity with the much stronger fake gravity pulling it down that much faster.
     
    Last edited: Nov 16, 2012
  3. biramani

    biramani

    Joined:
    Nov 19, 2012
    Posts:
    1
    how do you use or tweak this code if i have to throw the projectile at the click of the mouse ?? and my projectile is not iterating. it is not throwing at a regular interval when it is supposed to iterate. and what if i have multiple targets to hit????
     
    Last edited: Nov 19, 2012
  4. MangeshK

    MangeshK

    Joined:
    Aug 27, 2013
    Posts:
    1
    Hi,
    Thank for the script. I would like to use your script. My requirement is kind of different. I don't want to use launch angle. Instead I want to use how much time it should take to reach the target. How I can reuse above script? Could you help me on the same please..
     
  5. idurvesh

    idurvesh

    Joined:
    Jun 9, 2014
    Posts:
    495


    Wow thanks for excellent script working neatly.I have one query,my enemy is moving only one direction i.e., Z,I want to throw granade at enemy,how can I calculate enemy's future position based on speed and throw granade which will accurately hit moving enemy?
     
  6. Ali-Nagori

    Ali-Nagori

    Joined:
    Apr 9, 2010
    Posts:
    151
    @Stephan B
    i really appreciate your generous help , it saved me a lot of trouble and time.
     
  7. KidNova

    KidNova

    Joined:
    Dec 28, 2015
    Posts:
    13
    Hi, guys. Tried using this method for a game I'm making, but I couldn't find a way to increase the speed of the ball? Whenever I change velocity (or anything else), the object just goes to a different location that's not the target. Anyone know how to increase the speed?
     
  8. idurvesh

    idurvesh

    Joined:
    Jun 9, 2014
    Posts:
    495
    Try increasing Gravity
     
  9. KidNova

    KidNova

    Joined:
    Dec 28, 2015
    Posts:
    13
    Thanks man, helped me to single out the issue. Turns out I was increasing the gravity in the script, but it was overriding that gravity because it's a public member that can be set within Unity.
     
  10. idurvesh

    idurvesh

    Joined:
    Jun 9, 2014
    Posts:
    495
    Glad your problem got solved
     
  11. spark-man

    spark-man

    Joined:
    May 22, 2013
    Posts:
    96
    Excellent!! Thanks alot.
     
  12. kashifkamil

    kashifkamil

    Joined:
    Apr 6, 2016
    Posts:
    4
    I am doing this on a 2d platformer. My problem is the opposite of @KidNova
    My projectile moves too fast that I cn barely see it. I tried REDUCING the gravity to insane amounts (0.000000000 something) but still its too fast. Its fo fast that I blink and miss it. Is there a way to modify this code to work in a 2d platformer over short distances?
     
  13. Nisar2

    Nisar2

    Joined:
    Jun 21, 2016
    Posts:
    1
    when i call StartCoroutine(SimulateProjectile()); in void OnCollisionEnter(Collision col) object does'nt go to target position
     
  14. Tolufoyeh

    Tolufoyeh

    Joined:
    Nov 13, 2014
    Posts:
    16
    It seems there might be something about the way Unity calculates things that has changed since this code was written. This is the second code I've found online that clearly worked for others years ago, but when I use it now, the projectile always overshoots the target position. Please can anyone confirm that this code still works for them right now?
     
  15. Tolufoyeh

    Tolufoyeh

    Joined:
    Nov 13, 2014
    Posts:
    16
    Fixed it! I just had to change
    if(input.GetButton("Fire1"))
    to
    if(input.GetButtonUp("Fire1"))
    so that the coroutine wasn't triggered multiple times
     
  16. KaizerGoreng

    KaizerGoreng

    Joined:
    Jan 20, 2014
    Posts:
    4
    Thank you Stephan-B for the codes. I was able to tweak Stephan-B's code and make it so that the projectile reaches the target as intended. I also used another code to determine the target's position from the firing position using:
    Distance = 2s^2*sin(theta)*cos(theta)/g

    My problem is I have no idea how to increase the speed of the projectile. I need the target to be calculated the same way but with faster projectile speed. Anyone have any idea on how this can be achieved?

    P/s: I tried increasing the gravity but then the projectile won't land in the target's position correctly.

    Also, any ideas on how to make the projectile to face the target when falling?
     
  17. spark-man

    spark-man

    Joined:
    May 22, 2013
    Posts:
    96
    Try this demo
    https://www.desmos.com/calculator/gm3scd9uij

    ay - gravity
    T - time
    R - range (distance)

    Try altering gravity and click play button at "T" to visualize speed.
    Range (distance) remains constant, no matter what the gravity is.
     
    Last edited: Feb 13, 2017
  18. atpkewl

    atpkewl

    Joined:
    Jun 30, 2015
    Posts:
    17
    Hi Everyone, i like this code and it achieve what I wanted to achieve which is to throw an object in an arc towards a target. How can I get the velocity after it hits the target ? at the moment it just fall straight down as the previous velocity from rigidbody is 0.

    It looks unrealistic to fall straight instead of continue from the previous velocity.

    https://www.dropbox.com/s/03jvfjmwt2s4n07/ProjectileThrow.mov?dl=0

    Appreciate your help
     
  19. KaizerGoreng

    KaizerGoreng

    Joined:
    Jan 20, 2014
    Posts:
    4
    Thanks for this enlightenment Aldo-V. I realised that I need to rescale my result if I increase the gravity. I was using gravity to measure distance as well. So increasing gravity alone before this would skew the distance produced by the formula. I just needed to make sure I multiply where needed to get the same result for distance.
     
  20. idurvesh

    idurvesh

    Joined:
    Jun 9, 2014
    Posts:
    495
    I added animation to achieve that effect.Basically after object hit ground start animation which does that job.
     
  21. simone9725

    simone9725

    Joined:
    Jul 19, 2014
    Posts:
    234
    is there a way to create 2d torret effect?
     
  22. KaizerGoreng

    KaizerGoreng

    Joined:
    Jan 20, 2014
    Posts:
    4
    Code (CSharp):
    1.  
    2. // Extract the X  Y component of the velocity
    3.         float Vx = Mathf.Sqrt(Player_Velocity) * Mathf.Cos(angle * Mathf.Deg2Rad);
    4.         float Vy = Mathf.Sqrt(Player_Velocity) * Mathf.Sin(angle * Mathf.Deg2Rad);
    5.  
    6. Player.Translate(Vx * Time.deltaTime, ((Vy - (gravity * elapse_time)) / 4) * Time.deltaTime, 0); // Actual movement of the player
    I modified the code to look like this as my scene is rotation i.e. forward is X axis. It works for one game but now I need the target to be moved to left and right depending on user input. I managed to move the target, but have no idea how to tell the projectile to slowly move left/right depending on input.

    I know I need to change the value 0 in the code above to something like Vz * Time.deltaTime but how do I get Vz?
     
  23. zero_null

    zero_null

    Joined:
    Mar 11, 2014
    Posts:
    159
    Hello, this works fine for the most part. My object moves in z direction so the both x and y axis matter for me. Can anyone suggest what should I put in the x field instead of 0 ?
    I don't fully understand the logic and really need help.

    Thanks
     
  24. ghufronhasan23

    ghufronhasan23

    Joined:
    Aug 17, 2016
    Posts:
    7
    how can i use to my kinect character ? please help for my thesis
    greeting
     
  25. berk_can

    berk_can

    Joined:
    Feb 8, 2016
    Posts:
    15
    Man you are lifesaver, I am gonna use your code sample in my project and definitely add you in credit, can you give an email or your page or something xD
     
    hallarazad likes this.
  26. knik85

    knik85

    Joined:
    Dec 13, 2015
    Posts:
    3
    Thanks, this works for me(but it requires to adjust linear drag on Rigidbody2D):

    float target_Distance = Vector3.Distance(transform.position + posOffest, PlayerPosition.position);
    Vector2 direction = (PlayerPosition.position - (transform.position + posOffest)).normalized;



    // Calculate the velocity needed to throw the object to the target at specified angle.
    float projectile_Velocity = target_Distance / (Mathf.Sin(2 * firingAngle * Mathf.Deg2Rad) / (Physics2D.gravity.y * -1));

    // Extract the X Y componenent of the velocity
    float Vx = Mathf.Sqrt(projectile_Velocity) * Mathf.Cos(firingAngle * Mathf.Deg2Rad);
    float Vy = Mathf.Sqrt(projectile_Velocity) * Mathf.Sin(firingAngle * Mathf.Deg2Rad);

    Vector2 vel = new Vector2(Vx * direction.x, Vy) ;

    GameObject go = Instantiate(granade, transform.position + posOffest, new Quaternion());
    go.GetComponent<Rigidbody2D>().AddForce(vel,ForceMode2D.Impulse);
    go.GetComponent<Rigidbody2D>().AddTorque(torque);
     
    phootrf and benraay like this.
  27. goutham12

    goutham12

    Joined:
    Jul 14, 2016
    Posts:
    53
    Thank you very much bro. This answer need to be hilighted.
     
  28. BigGameCompany

    BigGameCompany

    Joined:
    Sep 29, 2016
    Posts:
    109
    Here's one that works if the start point and the target are at different y positions.

    Code (CSharp):
    1.  
    2. float gravity = 60f;
    3.  
    4. IEnumerator SimulateProjectileCR(Transform projectile, Vector3 startPosition, Vector3 endPosition)
    5.     {
    6.         projectile.position = startPosition;
    7.         float arcAmount = 8f;
    8.         float heightOfShot = 12f;
    9.         Vector3 newVel = new Vector3();
    10.         // Find the direction vector without the y-component
    11.         Vector3 direction = new Vector3(endPosition.x, 0f, endPosition.z) - new Vector3(startPosition.x, 0f, startPosition.z);
    12.         // Find the distance between the two points (without the y-component)
    13.         float range = direction.magnitude;
    14.  
    15.         // Find unit direction of motion without the y component
    16.         Vector3 unitDirection = direction.normalized;
    17.         // Find the max height
    18.    
    19.         float maxYPos = startPosition.y + heightOfShot;
    20.    
    21.         // if it has, switch the height to match a 45 degree launch angle
    22.         if (range / 2f > maxYPos)
    23.             maxYPos = range / arcAmount;
    24.         //fix bug when shooting on tower
    25.         if (maxYPos - startPosition.y <= 0)
    26.         {
    27.             maxYPos = startPosition.y + 2f;
    28.         }
    29.         //fix bug caused if we can't shoot higher than target
    30.         if (maxYPos - endPosition.y <= 0)
    31.         {
    32.             maxYPos = endPosition.y + 2f;
    33.         }
    34.         // find the initial velocity in y direction
    35.         newVel.y = Mathf.Sqrt(-2.0f * -gravity * (maxYPos - startPosition.y));
    36.         // find the total time by adding up the parts of the trajectory
    37.         // time to reach the max
    38.         float timeToMax = Mathf.Sqrt(-2.0f * (maxYPos - startPosition.y) / -gravity);
    39.         // time to return to y-target
    40.         float timeToTargetY = Mathf.Sqrt(-2.0f * (maxYPos - endPosition.y) / -gravity);
    41.         // add them up to find the total flight time
    42.         float totalFlightTime = timeToMax + timeToTargetY;
    43.         // find the magnitude of the initial velocity in the xz direction
    44.         float horizontalVelocityMagnitude = range / totalFlightTime;
    45.         // use the unit direction to find the x and z components of initial velocity
    46.         newVel.x = horizontalVelocityMagnitude * unitDirection.x;
    47.         newVel.z = horizontalVelocityMagnitude * unitDirection.z;
    48.    
    49.         float elapse_time = 0;
    50.         while (elapse_time < totalFlightTime)
    51.         {
    52.             projectile.Translate(newVel.x * Time.deltaTime, (newVel.y - (gravity * elapse_time)) * Time.deltaTime, newVel.z * Time.deltaTime);
    53.             elapse_time += Time.deltaTime;
    54.             yield return null;
    55.         }
    56.    
    57.  
    58.     }
     
    yano_123 likes this.
  29. SamRock

    SamRock

    Joined:
    Sep 5, 2017
    Posts:
    250
    Hey! How do I convert this to work for 3D? Also how do you calculate the torque?
     
  30. huangyunfeng

    huangyunfeng

    Joined:
    Apr 5, 2019
    Posts:
    5
    hi,BigGameCompany
    what mean about this line, is a formula or somewhat ?

    float timeToMax = Mathf.Sqrt(-2.0f * (maxYPos - startPosition.y) / -gravity);

    very thanks !
     
  31. SamRock

    SamRock

    Joined:
    Sep 5, 2017
    Posts:
    250
    SirFlubbhuff likes this.
  32. ShervinM

    ShervinM

    Joined:
    Sep 16, 2017
    Posts:
    67
    Very nice, this was a good refresher of my grade 12 physics!

    I slightly adapted your code to use the general projectile range formula for anyone that needs to do parabolas with different start / end heights.

    For how the velocity is calculated, check out the range formula:
    https://en.wikipedia.org/wiki/Range_of_a_projectile

    Code (CSharp):
    1.         IEnumerator ParabolicMotion(Transform projectileTransform, Vector3 target)
    2.         {
    3.             // Calculate the range from the projectile to the target by zero-ing each out in their y-axis
    4.             Vector3 zeroedOrigin = new Vector3(projectileTransform.position.x, 0, projectileTransform.position.z);
    5.             Vector3 zeroedTarget = new Vector3(target.x, 0, target.z);
    6.             Vector3 zeroedDirection = (zeroedTarget - zeroedOrigin).normalized;
    7.  
    8.             float angleRad = firingAngle * Mathf.Deg2Rad;
    9.             float heightDifference = projectileTransform.y - target.y;
    10.             float targetDistance = Vector3.Distance(projectileTransform.position, target);
    11.             float targetRange = Vector3.Distance(zeroedOrigin, zeroedTarget);
    12.  
    13.             // Calculate the velocity needed to throw the object to the target at specified angle.
    14.             // Velocity can be solved by re-arranging the general equation for parabolic range:
    15.             // https://en.wikipedia.org/wiki/Range_of_a_projectile
    16.             float projectile_Velocity
    17.                 = (Mathf.Sqrt(2) * targetRange * Mathf.Sqrt(gravity) * Mathf.Sqrt(1 / (Mathf.Sin(2 * angleRad)))) /
    18.                   (Mathf.Sqrt((2 * targetRange) + (heightDifference * Mathf.Sin(2 * angleRad) * (1 / Mathf.Sin(angleRad)) * (1 / Mathf.Sin(angleRad)))));
    19.  
    20.             // Extract the X  Y componenent of the velocity
    21.             float Vx = projectile_Velocity * Mathf.Cos(angleRad);
    22.             float Vy = projectile_Velocity * Mathf.Sin(angleRad);
    23.  
    24.             // Calculate flight time.
    25.             float flightDuration = targetRange / Vx;
    26.  
    27.             // Rotate projectile to face the target.
    28.             projectileTransform.rotation = Quaternion.LookRotation(zeroedDirection);
    29.  
    30.             float elapse_time = 0;
    31.  
    32.             while (elapse_time < flightDuration)
    33.             {
    34.                 transform.Translate(Vx * Time.deltaTime, (Vy - (gravity * elapse_time)) * Time.deltaTime, 0);
    35.  
    36.                 elapse_time += Time.deltaTime;
    37.  
    38.                 yield return null;
    39.             }
    40.         }
     
    Last edited: Jan 14, 2021
    Nit_Ram likes this.
  33. hainogames

    hainogames

    Joined:
    Apr 20, 2020
    Posts:
    2
    Thank you So much Guys for this awesome discussion & scripts for this mechanic.
    It helped me a lot.
     
  34. dmoret

    dmoret

    Joined:
    Aug 23, 2017
    Posts:
    13
    I don't get it, how can that code work with this line:

    Code (CSharp):
    1. Projectile.Translate(0, (Vy - (gravity * elapse_time)) * Time.deltaTime, Vx * Time.deltaTime);
    The x translation is always 0, so the projectile will never move along the x axis, or did I miss something?
     
  35. ShervinM

    ShervinM

    Joined:
    Sep 16, 2017
    Posts:
    67
    Hi @dmoret, I should have cleaned that up a bit. In my case I've set x translation to zero because I was solving this for a 3d game. So I was using the Z axis to move things forward, hence the "Vx * Time.deltaTime".

    So just move that over to the X axis and zero out the Z like so:

    Code (CSharp):
    1. Projectile.Translate(Vx * Time.deltaTime, (Vy - (gravity * elapse_time)) * Time.deltaTime, 0);
    I've fixed my previous post now.
     
    Last edited: Jan 14, 2021
  36. dmoret

    dmoret

    Joined:
    Aug 23, 2017
    Posts:
    13
    Thanks for the quick reply. But I also need it to work in 3D, so zeroing out the Z doesn't work either, the projectile needs to move in all axis.
     
  37. ShervinM

    ShervinM

    Joined:
    Sep 16, 2017
    Posts:
    67
    Well even in 3D all you have to do is simply decide along which two directions you want to move your projectile in a parabolic motion.

    One option is to always face one of the + local axes (often + Z) of your object towards the horizontal direction of movement before you cast your parabolic motion.

    Dont forget that Transform.Translate is by default set to relative = self. That means that it applies the translation relative to the transforms local axes.

    So, imagine you have two objects A and B. You want to move A in a parabolic motion towards B. What you can do is rotate A such that its local +Z axis is facing B, then cast the projectile in the parabolic motion (as shown in the code above), but use only the Y and Z axis in the translate code:

    Code (CSharp):
    1. Projectile.Translate(0, (Vy - (gravity * elapse_time)) * Time.deltaTime, Vx * Time.deltaTime);
    This indeed assumes that the objects local Y axis is parallel with the world Y axis (Which defines up and down in your game).
     
  38. dmoret

    dmoret

    Joined:
    Aug 23, 2017
    Posts:
    13
    I did forget. Thank you.
     
  39. jeffman1

    jeffman1

    Joined:
    Oct 21, 2013
    Posts:
    36
    I am trying to make this be more accurate and render a line separately for aiming like a grenade arc except I am using this for throwing a stone in a sling. I have an AI character to test on already but need it to hit his head. So, how can I make this move to the players right side a little? (I am using the third person user control script and have it throwing but its a little off and low). Also, I might use arrows later and need to have this changed for that. I found this also that is really interesting:
    Projectile Motion Tutorial for Arrows and Missiles in Unity3D – Volkan Ilbeyli – Graphics Programmer (vilbeyli.github.io)
     
  40. trojanowski1

    trojanowski1

    Joined:
    May 4, 2021
    Posts:
    1
    Whenever my enemy attacks, the coroutine starts to throw the projectile but then once it attacks again the previous coroutine stops the previous projectile along its trajectory, is there any way to fix this?
     
  41. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,520
    Yes, and it involves NOT necro-posting to a ten year old thread.

    Go start your own fresh thread and explain your problem fully. Nobody wants to play 20 questions.

    How to report your problem productively in the Unity3D forums:

    http://plbm.com/?p=220

    If you post a code snippet, ALWAYS USE CODE TAGS:

    How to use code tags: https://forum.unity.com/threads/using-code-tags-properly.143875/
     
Thread Status:
Not open for further replies.