Search Unity

Resolved Not reaching exact jump height using kinematic equation? (following Seb Lague's kinematic tutorials)

Discussion in 'Physics' started by tfishell, Jan 14, 2021.

  1. tfishell

    tfishell

    Joined:
    Nov 20, 2017
    Posts:
    97
    I'm following along Sebastian Lague's "Kinematic Equations"/SUVAT short series of videos and trying to apply them in Unity. I'm somewhat stuck in the first video trying to get a character to jump 3 meters. (I skipped to the third video to get an idea of how to take the knowledge and use it in Unity / C#.)

    Overall the code is basically working correctly, but I've noticed that the character doesn't actually reach a displacement of 3 meters, instead closer to 2.9. (like 0.01499969 to the apex at 2.906404)

    The values I'm using are the same ones used in the first video (unless I missed something of course). Granted I didn't do the calculations in-code but afaik that shouldn't be a problem here.



    (Screenshot of the Video #3 code that I tried to modify for this jump/Video #1 example; I thought it too big to post here)

    Code (CSharp):
    1. //make character jump using suvat equation 5
    2. public class JumpCharacter : MonoBehaviour {
    3.  
    4.     private Rigidbody2D rb2d;
    5.  
    6.     private float finalVelocity = 0; //0 m/s
    7.     private float displacement = 3; //+3 m
    8.  
    9.     private float initialVelocity;
    10.     private float accelerationDueToGravity = -20; //(m/s)^2
    11.  
    12.  
    13.     // Use this for initialization
    14.     void Start () {
    15.         rb2d = GetComponent<Rigidbody2D>();
    16.  
    17.         initialVelocity = Mathf.Sqrt(120);
    18.         Debug.Log(initialVelocity.ToString("F30"));
    19.     }
    20.  
    21.     public void Update()
    22.     {
    23.         if (Input.GetKeyDown(KeyCode.Space))
    24.         {
    25.             Launch();
    26.         }
    27.     }
    28.  
    29.     void Launch()
    30.     {
    31.         Physics2D.gravity = Vector2.up * accelerationDueToGravity;
    32.         rb2d.gravityScale = 1;
    33.         rb2d.velocity = initialVelocity * Vector2.up;
    34.     }
    35. }
    Gif of character jumping and transform
    Rigidbody2D screenshot

    Should I be doing something differently (in the code, or something like changing Rigidbody2D properties)? Again, it seems to basically be working as expected other than not quite reaching the apex.

    Thanks for any help.
     
    Last edited: Jan 14, 2021
  2. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    I ran these tests in 2020.2.1f1

    I changed your code a little to capture what was happening at each physics step:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. //make character jump using suvat equation 5
    6. public class JumpCharacter : MonoBehaviour
    7. {
    8.  
    9.     private Rigidbody2D rb2d;
    10.  
    11.     private float finalVelocity = 0; //0 m/s
    12.     private float displacement = 3; //+3 m
    13.  
    14.     private float initialVelocity;
    15.     private float accelerationDueToGravity = -20; //(m/s)^2
    16.  
    17.     private bool dataLog = false;
    18.     private int fixedUpdates = 0;
    19.  
    20.  
    21.     // Use this for initialization
    22.     void Start()
    23.     {
    24.         rb2d = GetComponent<Rigidbody2D>();
    25.  
    26.         initialVelocity = Mathf.Sqrt(120);
    27.         Debug.Log("Initial Velocity: " + initialVelocity.ToString("F30"));
    28.     }
    29.  
    30.     public void Update()
    31.     {
    32.         if (Input.GetKeyDown(KeyCode.Space))
    33.         {
    34.             Launch();
    35.         }
    36.     }
    37.  
    38.  
    39.     public void FixedUpdate()
    40.     {
    41.         if (dataLog)
    42.         {
    43.             float time = fixedUpdates * Time.fixedDeltaTime;
    44.             float vel = rb2d.velocity.y;
    45.             float pos = rb2d.transform.position.y;
    46.             Debug.Log("Step: " + fixedUpdates + " Time: " + time + " Velocity: " + vel +" Position: " + pos);
    47.             if (vel < -0.1f) dataLog = false;
    48.             fixedUpdates++;
    49.         }
    50.     }
    51.  
    52.  
    53.     void Launch()
    54.     {
    55.         dataLog = true;
    56.         Physics2D.gravity = Vector2.up * accelerationDueToGravity;
    57.         rb2d.gravityScale = 1;
    58.         rb2d.velocity = initialVelocity * Vector2.up;
    59.     }
    60. }
    61.  
    Then I got the output and compared it to the hand calculations for each step in a spreadsheet... Note the Pos value is taken from the next output line as it requires the physics solver to have run, I've just moved them all up a line to match the hand calcs. This could be a bit confusing as we later establish that this pos is calculated from the updated velocity (which is still listed on the following line).

    upload_2021-1-14_10-19-46.png

    That was unexpected for me... the Unity Physics model perfectly matched the velocity prediction, but produced a precise delta of 0.004m every step of the position calculation. Note, the offset that you saw of 0.015m is the collider thickness offset, add this gap between your rb and supporting surface to remove the effect.

    This made me think perhaps there was a small error in the 2D physics engine, so I ran the same experiment in 3D... and got the same answers as the 2D engine, which made me think there was nothing wrong with Unity and I had made an incorrect assumption somewhere.

    Let's see if someone smarter comes along!

    EDIT
    I thought about this a bit more and I believe the 0.004m delta is due to the physics engine assuming that velocity is constant for the physics cycle when calculating displacement (i.e. not including the 0.5 at^2 part).
     
    Last edited: Jan 15, 2021
    SparrowGS, tfishell and MelvMay like this.
  3. BakeMyCake

    BakeMyCake

    Joined:
    May 8, 2017
    Posts:
    175
    @AlTheSlacker
    A possible cause of systemic error is described in the article below. See the section on Euler integration where the author makes a somewhat similar calculation experiment and explains the results:
    https://gafferongames.com/post/integration_basics/

    I can't find a definitive answer on which integration PhysX is using, but some unconfirmed sources suggest it's a derivative of Euler, and if that is the case, you are pretty much correct about the engine assuming constant velocity.

    On a side note, why 20 for acceleration due to gravity?
     
    SparrowGS, tfishell, Maeslezo and 2 others like this.
  4. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    What a great paper, thanks for that link, I think it perfectly explains what we are seeing here.

    The 20 m/s/s comes from the original tutorial, I believe it is just chosen as a whole number to keep the maths easy and 20 instead of 10 to show that a game does not need to be the same as earth like.
     
    tfishell likes this.
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,456
    Very nice and well presented article that.

    Not sure how much it helps but Box2D uses a semi-implicit Euler approach. The crux of the work is done here integrating acceleration to give velocity (also adding in user-forces, gravity & damping terms) followed by contact solving and finally integrating velocity to give position.

    In terms of testing above, you can get finer control by performing manual simulation rather than waiting on the FixedUpdate by using Physics2D.Simulate.

    Hope that helps a little.
     
    Last edited: Jan 15, 2021
  6. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    I found the 2D and 3D solutions near enough the same, so I think it is a safe bet they are both using semi-implicit Euler.
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,456
    Yes, PhysX also uses a semi-implicit Euler from my understanding.
     
    AlTheSlacker likes this.
  8. tfishell

    tfishell

    Joined:
    Nov 20, 2017
    Posts:
    97
    Thanks for taking the time to respond, everyone. I'll read over the posts more and read/study the Gaffer On Games article soon. (Sounds like "semi-implicit Euler" in the article is especially relevant here; I have basic experience with Euler angles and pitch-yaw-roll from another tutorial)

    I dunno if I should have stated early on I don't have much experience with physics (still early on in the Khan academy reading). If someone could give a basic overview of what I currently need to know, I'd appreciate that.

    For now, I get the sense that making changes to be more accurate to the kinematic equation to get a 3 displacement are very much out of my hands (other than increasing the initial velocity some amount), as this would involve having to mess with Unity's underlying code. So presumably I need to simply accept what's happening (which I'm basically fine with; I was mainly posting here to see if there were errors and changes I was likely capable of fixing/making).
     
  9. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    If you look at the first line of my spreadsheet you will see initial velocity is 10.95445 m/s and that after one physics step it becomes 10.55445 m/s that's your acceleration of -20 m/s/s being applied for 0.02 s (the FixedDeltaTime). Using the semi-Euler approach the engine is calculating displacement based on a constant velocity of 10.55445 m/s, so it is missing the displacement that occurred due to the acceleration from the higher velocity. You just need to bump the initial velocity up enough to make it use the average of the two velocities to calculate displacement.

    In short + 0.2 m/s to your initial velocity will give you the correct displacement.

    That correction factor is 0.5 * FixedDeltaTime * acceleration * -1
    The -1 is because you are working backwards from the final velocity (just a sign change)
    The 0.5 is to get you the average of the two velocities.
     
    Last edited: Jan 16, 2021
    tfishell likes this.
  10. tfishell

    tfishell

    Joined:
    Nov 20, 2017
    Posts:
    97
    Thanks for the explanation, I appreciate it. I'll keep looking over/studying this thread and related material and try to really comprehend what's going on (maybe one day :p).

    "s = ut + 0.5 at ^2" is just SUVAT equation 3 written slightly differently, right? (equals "s = ut + (at^2)/2")
     
    AlTheSlacker likes this.