Throw an object along a parabola?

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

Not open for further replies.
1. 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:

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

Unity Technologies

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

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

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

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

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

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

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

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

Joined:
Jun 9, 2014
Posts:
495

11. spark-man

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

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

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

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

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

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

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

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

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

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

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

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

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

Joined:
Aug 17, 2016
Posts:
7
greeting

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

26. 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());

benraay likes this.
27. goutham12

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

28. BigGameCompany

Joined:
Sep 29, 2016
Posts:
95
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);
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

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

Joined:
Apr 5, 2019
Posts:
5
hi,BigGameCompany

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

very thanks !

31. SamRock

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

Joined:
Sep 16, 2017
Posts:
60
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.
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

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

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

Joined:
Sep 16, 2017
Posts:
60
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

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

Joined:
Sep 16, 2017
Posts:
60
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

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

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

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

Joined:
Mar 16, 2013
Posts:
29,891
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/