How to calculate force needed to jump towards target point?

Discussion in 'Physics' started by TaurusSilver, Dec 7, 2015.

1. Hi all,

So things that I know/got:
- my object's position (start position) and the target position (end position)
- direction (target - my object position ?)

What I need to know is the force that is required to get/jump to the target position (rigidbody.AddForce ( requiredForce * direction, ForceMode.Impulse);

I have already tried to look around and found the following threads:

They calculate the arc but that is not exactly what I need.

It would be great if someone knows the calculation for this and can explain if it is a difficult formula (I'm not that good with physics calculation).

Thanks.

siddharth3322 likes this.
2. Iron-Warrior

Joined:
Nov 3, 2009
Posts:
672
I don't use rigidbodies, but the same principles should apply in the code below:

Code (csharp):
1. // Calculate the initial velocity of a jump based off gravity and desired maximum height attained
2. private float CalculateJumpSpeed(float jumpHeight, float gravity)
3. {
4.     return Mathf.Sqrt(2 * jumpHeight * gravity);
5. }
...and you plug in the height you want to hit with your jump, and your current gravity magnitude.

3. I tested it, but I could guess that this is not exactly what I need.
Code (CSharp):
1.    void JumpTowardPoint()
2.     {
3.         float gravity = Physics.gravity.magnitude;
4.         float initialVelocity = CalculateJumpSpeed(jumpHeight, gravity);
5.
6.         Vector3 direction = target.transform.position - transform.position;
7.
8.         rigidbody.AddForce(initialVelocity * direction, ForceMode.Impulse);
9.     }
10.
11.     private float CalculateJumpSpeed(float jumpHeight, float gravity)
12.     {
13.         return Mathf.Sqrt(2 * jumpHeight * gravity);
14.     }
This will shoot just shoot my object in the direction of the target and just fly over it. What I actually want is that it 'jump'/go to that target spot.

Last edited: Dec 8, 2015
4. Iron-Warrior

Joined:
Nov 3, 2009
Posts:
672
beuz likes this.
5. Yes, that's what I mean. Sorry if I wasn't all too clear with my explanation.

6. Iron-Warrior

Joined:
Nov 3, 2009
Posts:
672
Okay, so the first thing to bear in mind is that the goal to solving this is to treat the object's motion along the two axes separately, which we'll call x and y. Although the results of this can be used in 3d space, this problem only concerns two axes, which is nice. Our given values (the ones we have) are the position of our object, position of the target, and the angle that we want to fire at. From this, we know the planar distance between the object (d) as well as the offset on the y axis between the two. We want the initial velocity to fire the object at to strike the target t. As stated above, we'll find the velocity of the components, x and y. vX (velocity x) is constant (there's no air resistance or anything) and so is the following:

Code (csharp):
1. v0 * cos(angle)
...where v0 is our initial velocity. We know that the distance an object travels is equal to it's velocity*time, so the distance x travels is vX*t, or how long our object is in the air.

vY is just as easy to solve for, giving us:

Code (csharp):
1. v0 * sin(angle)
However, y is not a constant velocity, but rather has the acceleration of gravity on it throughout it's time in the air. So the velocity of y is equal to the initial velocity, PLUS however much acceleration has acted on it over the course of the time in the air.

Code (csharp):
1. vY = -gt + v0 * sin(angle)
So as you can see, at t=0, y's velocity is just the initial velocity, but over time more acceleration is added. Our y position is then:

Code (csharp):
1. -1/2*g*t^2 + v0 * sin(angle)*t + y0
Now that we can calculate the position of our object on either two axes at any time, we can create a formula to solve for initial velocity. Our initial position is equal to x0 = 0, and y0 = yOffset. We also know that the final x position we want is just equal to distance, and the final y position is equal to 0. We can insert all these into the above two equations, and solve for the both of them (which eliminates t) to get the following:

Code (csharp):
1. v0 = (1 / Mathf.Cos(angle)) * Mathf.Sqrt((0.5f * gravity * Mathf.Pow(distance, 2)) / (distance * Mathf.Tan(angle) + yOffset));
It's a bit to take in! I had to figure this our a few years back for a project, and I got most of the info from this physics exchange post. Implementing the above in Unity looks like this:

Code (csharp):
1.
2. using UnityEngine;
3. using System.Collections;
4.
5. public class ProjectileFire : MonoBehaviour {
6.
7.     [SerializeField]
8.     Transform target;
9.
10.     [SerializeField]
11.     float initialAngle;
12.
13.     void Start () {
14.         var rigid = GetComponent<Rigidbody>();
15.
16.         Vector3 p = target.position;
17.
18.         float gravity = Physics.gravity.magnitude;
19.         // Selected angle in radians
20.         float angle = initialAngle * Mathf.Deg2Rad;
21.
22.         // Positions of this object and the target on the same plane
23.         Vector3 planarTarget = new Vector3(p.x, 0, p.z);
24.         Vector3 planarPostion = new Vector3(transform.position.x, 0, transform.position.z);
25.
26.         // Planar distance between objects
27.         float distance = Vector3.Distance(planarTarget, planarPostion);
28.         // Distance along the y axis between objects
29.         float yOffset = transform.position.y - p.y;
30.
31.         float initialVelocity = (1 / Mathf.Cos(angle)) * Mathf.Sqrt((0.5f * gravity * Mathf.Pow(distance, 2)) / (distance * Mathf.Tan(angle) + yOffset));
32.
33.         Vector3 velocity = new Vector3(0, initialVelocity * Mathf.Sin(angle), initialVelocity * Mathf.Cos(angle));
34.
35.         // Rotate our velocity to match the direction between the two objects
36.         float angleBetweenObjects = Vector3.Angle(Vector3.forward, planarTarget - planarPostion);
37.         Vector3 finalVelocity = Quaternion.AngleAxis(angleBetweenObjects, Vector3.up) * velocity;
38.
39.         // Fire!
40.         rigid.velocity = finalVelocity;
41.
42.         // Alternative way:
43.         // rigid.AddForce(finalVelocity * rigid.mass, ForceMode.Impulse);
44.     }
45. }
46.
Which also rotates our velocity so you can fire in any direction, since we only solve it for the x and y plane.

7. Thanks for the explanation and how to implement. I tested it out but I encountered the following 2 scenarios:
- by using rigidbody.velocity, after the projectile object fires and the x and z are equal to the x and y of the target, the object will slowly goes down (which isn't what we want because of physics)
-by using the addforce, the projectile object will shoots up and keeps the force applied to it(?). So it will never hit the target?

I think that I need a way to find when the projectile has reached the max height of the trajectory and set the velocity back to 0 or make it decrease? I'm not really confident with my physics knowledge but because of gravity, after reaching the max height, shouldn't the gravity stop the acceleration of the force and bring it back to 0 and make the object fall down again?

My object did not 'look' in the direction of the target (instead of the blue arrow in unity, the red arrow was pointing towards the target). Do I have to set the rotation myself like this?
Code (CSharp):
1. Vector3 rotateValue = target.transform.position - transform.position;
2.         Quaternion lookRotation = Quaternion.LookRotation(rotateValue);
3.         transform.rotation = lookRotation; Last edited: Dec 10, 2015
siddharth3322 likes this.
8. Iron-Warrior

Joined:
Nov 3, 2009
Posts:
672
TakuanDaikon and OneDragutin like this.
9. No idea what I did wrong yesterday but it works now. Thanks for the help.

10. Pawl

Joined:
Jun 23, 2013
Posts:
109
11. @Iron-Warrior Thanks for the code and the explanation. I just have one question: was supposed to work all directions? When I use here, if the object is on the rigth of the target, for example, the force is inverse, and the object go to rigth, instead go to left, where is the target.

12. While his code is very helpful, it wasn't perfect. To be a bit more specific, when the velocity needs to push the object along the world space X axis, is was unable to apply a negative X velocity, resulting in the vector never being able to go left (relative to world space).

To fix this, check if the object position x is greater than the target position x (i.e. check if going in a negative X direction is required to reach the target) and, if so, inverse the angleBetweenObjects (i.e. mulltiple by -1).

Before Code Change
Code (csharp):
1. // From
2. float angleBetweenObjects = Vector3.Angle(Vector3.forward, planarTarget - planarPostion);
3. // To (added "* (p.x > transform.position.x ? 1 : -1)")
4. float angleBetweenObjects = Vector3.Angle(Vector3.forward, planarTarget - planarPostion) * (p.x > transform.position.x ? 1 : -1);
After Last edited: Nov 13, 2016
13. This thread is pretty old, but does someone know how to increase the speed of the jump using the script from Iron-Warrior without changing the destination. If you increase the addforce, it won't go to the desired destination, but I want the time of the jump to be shorter. I can't use timeScale because the other objects shouldn't go faster.

siddharth3322 likes this.
14. Iron-Warrior's script calculates the velocity of the projectile based on an initial angle and the target destination. The only physical way for it to get their faster is to use a smaller (flatter) angle.

But you could try using an alternate gravity force. If you use a stronger gravity it will get there faster. If you don't want to adjust the global gravity you could specify the gravity force in the script, but you'd have to disable gravity on the rigidbody and do your own gravity in fixed update.

Edit:
Or maybe you're looking to start with an initial velocity and want to figure out the angle need to hit a target? I think I've done that before but I can't remember how to do it at the moment.

Last edited: Jan 23, 2017
siddharth3322 likes this.
15. Thnx. I changed the global gravity to -100 and the speed is okay now. I don't know if this gravity is bad?

16. Hi @Zethros & @Iron-Warrior , thank you very much for sharing the codes.
Just wondering how do I draw the dotted line that sort of predicts where the ball will travel before shots ?
similar to Angry birds before you release the catapult.

17. Sorry, but I never coded something like that before. I'll leave that answer to someone else

(Sorry for the delayed response. I didn't get a notification from you mentioning me for some reason.)
(Also sorry for replying without an answer. I just didn't want people who could answer to think they shouldn't because you asked Iron-Warrior and I so specifically)

18. Maybe let a gameobject automatically jump to the destination with a trail behind it?

19. Iron-Warrior

Joined:
Nov 3, 2009
Posts:
672
If anyone is interested, I was planning on writing an article about projectile trajectories but it's kind of fell on the wayside due to other obligations. Anyways, I made an example project for the article that allows you to fire a projectile at either a fixed angle or a fixed velocity and draws out that trajectory. Bang bang. Anyways, it mostly boils down to this ProjectileMath class I wrote:

Code (csharp):
1.
2. using UnityEngine;
3.
4. public static class ProjectileMath
5. {
6.     public static bool LaunchAngle(float speed, float distance, float yOffset, float gravity, out float angle0, out float angle1)
7.     {
8.         angle0 = angle1 = 0;
9.
10.         float speedSquared = speed * speed;
11.
12.         float operandA = Mathf.Pow(speed, 4);
13.         float operandB = gravity * (gravity * (distance * distance) + (2 * yOffset * speedSquared));
14.
15.         // Target is not in range
16.         if (operandB > operandA)
17.             return false;
18.
19.         float root = Mathf.Sqrt(operandA - operandB);
20.
21.         angle0 = Mathf.Atan((speedSquared + root) / (gravity * distance));
22.         angle1 = Mathf.Atan((speedSquared - root) / (gravity * distance));
23.
24.         return true;
25.     }
26.
27.     /// <summary>
28.     /// Calculates the initial launch speed required to hit a target at distance with elevation yOffset.
29.     /// </summary>
30.     /// <param name="distance">Planar distance from origin to the target</param>
31.     /// <param name="yOffset">Elevation of the origin with respect to the target</param>
32.     /// <param name="gravity">Downwards force of gravity in m/s^2</param>
33.     /// <param name="angle">Initial launch angle in radians</param>
34.     public static float LaunchSpeed(float distance, float yOffset, float gravity, float angle)
35.     {
36.         float speed = (distance * Mathf.Sqrt(gravity) * Mathf.Sqrt(1 / Mathf.Cos(angle))) / Mathf.Sqrt(2 * distance * Mathf.Sin(angle) + 2 * yOffset * Mathf.Cos(angle));
37.
38.         return speed;
39.     }
40.
41.     public static Vector2[] ProjectileArcPoints(int iterations, float speed, float distance, float gravity, float angle)
42.     {
43.         float iterationSize = distance / iterations;
44.
45.         float radians = angle;
46.
47.         Vector2[] points = new Vector2[iterations + 1];
48.
49.         for (int i = 0; i <= iterations; i++)
50.         {
51.             float x = iterationSize * i;
52.             float t = x / (speed * Mathf.Cos(radians));
53.             float y = -0.5f * gravity * (t * t) + speed * Mathf.Sin(radians) * t;
54.
55.             Vector2 p = new Vector2(x, y);
56.
57.             points[i] = p;
58.         }
59.
60.         return points;
61.     }
62. }
63.

You can get the full project at the GitHub repo here.

TimCoster, LAKSHAYMAVIA, pcg and 8 others like this.
20. Sebastian Lague have some good videos on kinematic equations, where one of them is related to this specific problem.

zingabo likes this.
21. Hi!! I have downloaded this GitHub repo, really helped for me. But I want to adjust Cannon's angle (and projectile also) using drag on Cannon (like angry Bird, but in 3D world).plz help, to convert it.....

22. PedroTalespin

Joined:
Jan 23, 2018
Posts:
1
This is awesome! Thanks for the project Iron-Warrior. Now to make it more complicated, how would one go about accounting for drag in the projectile's rigidbody?

23. tonimarquez84

Joined:
Feb 23, 2015
Posts:
13
I've realized you use Quaternion.AngleAxis but you must use Quaternion.SignedAngle to avoid it returns only positive angles.

24. Hello everyone!

I'm trying to use this part of the warior script.
But my project is 2D. I adapted the script to work in 2D but im having difficulty with YOffset. I can not make it work..

My changed script is this!

Code (CSharp):
1.
2.  private Vector2 GetIntentPower(Vector2 target, float initialAngle)
3.         {
4.
5.             float gravity = Physics.gravity.magnitude;
6.             // Selected angle in radians
7.
8.             // Planar distance between objects
9.             Vector2 p1 = new Vector2(transform.position.x,transform.position.y);
10.             Vector2 p2 = new Vector2(target.x,transform.position.y);
11.             float Xdistance = Vector2.Distance(p1, p2);
12.
13.             // Distance along the y axis between objects
14.             float yOffset = transform.position.y-target.y; //Its not work
15.
16.
17.             float angle = initialAngle * Mathf.Deg2Rad;
18.
19.             float initialVelocity = (1 / Mathf.Cos(angle)) * Mathf.Sqrt((0.5f * gravity * Mathf.Pow(Xdistance, 2)) / (Xdistance * Mathf.Tan(angle) + yOffset));
20.
21.             Vector2 velocity = new Vector2(initialVelocity * Mathf.Sin(angle), initialVelocity * Mathf.Cos(angle));
22.
23.             // Rotate our velocity to match the direction between the two objects
24.             float angleBetweenObjects = Vector2.Angle(Vector2.right, p2 - p1);
25.             Vector2 finalVelocity = Quaternion.AngleAxis(angleBetweenObjects, Vector2.right) * velocity;
26.
27.             // Fire!
28.             //rigid.velocity = finalVelocity;
29.             return finalVelocity;
30.
31.             // Alternative way:
32.             // rigid.AddForce(finalVelocity * rigid.mass, ForceMode.Impulse);
33.         }
34.

It works perfectly when the PLAYER is on the same Y Position as the Target Y Position. But if the Player Y is elsewhere, then it does not work.

Could you help with this (^.~) Thankx !