Search Unity

How to calculate force needed to jump towards target point?

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

  1. TaurusSilver

    TaurusSilver

    Joined:
    Sep 1, 2015
    Posts:
    10
    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:
    http://answers.unity3d.com/questions/148399/shooting-a-cannonball.html
    http://answers.unity3d.com/questions/145972/how-to-make-enemy-canon-ball-fall-on-mooving-targe.html

    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

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    670
    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. TaurusSilver

    TaurusSilver

    Joined:
    Sep 1, 2015
    Posts:
    10
    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

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    670
    Hey! Sorry if I didn't understand right. So what you're looking for is essentially a "cannon-ball" type problem, where you are firing launching your player (the cannonball) at the target...like this?



    It's pretty late but if that's the idea, I'll write up a more detailed answer tomorrow.
     
    beuz likes this.
  5. TaurusSilver

    TaurusSilver

    Joined:
    Sep 1, 2015
    Posts:
    10
    Yes, that's what I mean. Sorry if I wasn't all too clear with my explanation.
     
  6. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    670
    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. TaurusSilver

    TaurusSilver

    Joined:
    Sep 1, 2015
    Posts:
    10

    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

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    670
    Hmm, not entirely sure what you're asking. The ball is "pushed" back down to zero by Unity's built in gravity (since you're using a rigidbody). Here's a gif of the function being run, with three different initial values. Was the script not working?

     
    TakuanDaikon and OneDragutin like this.
  9. TaurusSilver

    TaurusSilver

    Joined:
    Sep 1, 2015
    Posts:
    10
    No idea what I did wrong yesterday but it works now. Thanks for the help.
     
  10. Pawl

    Pawl

    Joined:
    Jun 23, 2013
    Posts:
    108
    @Iron-Warrior Thanks for the in-depth explanation, it worked like a charm! =)



     
  11. rafaelferrari1

    rafaelferrari1

    Joined:
    Jan 6, 2014
    Posts:
    7
    @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. Zethros

    Zethros

    Joined:
    Jan 11, 2013
    Posts:
    5
    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. Jip1912

    Jip1912

    Joined:
    Mar 13, 2015
    Posts:
    314
    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. jmjd

    jmjd

    Joined:
    Nov 14, 2012
    Posts:
    42
    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. Jip1912

    Jip1912

    Joined:
    Mar 13, 2015
    Posts:
    314
    Thnx. I changed the global gravity to -100 and the speed is okay now. I don't know if this gravity is bad?
     
  16. atpkewl

    atpkewl

    Joined:
    Jun 30, 2015
    Posts:
    15
    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. Zethros

    Zethros

    Joined:
    Jan 11, 2013
    Posts:
    5
    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. Jip1912

    Jip1912

    Joined:
    Mar 13, 2015
    Posts:
    314
    Maybe let a gameobject automatically jump to the destination with a trail behind it?
     
  19. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    670
    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. EnokV

    EnokV

    Joined:
    Apr 14, 2016
    Posts:
    51
    Sebastian Lague have some good videos on kinematic equations, where one of them is related to this specific problem.

     
    zingabo likes this.
  21. InfinityFleck

    InfinityFleck

    Joined:
    Apr 23, 2016
    Posts:
    1
    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

    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

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

    neoneper

    Joined:
    Nov 14, 2013
    Posts:
    42
    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 !