Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Transform continues moving in tiny increments even when velocity goal is 0

Discussion in 'Scripting' started by tytbone, Aug 29, 2019.

  1. tytbone

    tytbone

    Joined:
    Jul 16, 2013
    Posts:
    18
    (edit: to be clear, this is just me trying to learn, not anything serious nor for an actual game.)

    I've been following some "math for game devs" video tutorials for a while (sort of converting the C++ to Unity/C#) but got stuck a few months back. It's a simple movement script where the player's velocity interpolates (based on deltaTime * scalar) up or down to the goal based on keyboard presses, and that velocity amount is set to the transform and next velocity.

    The dev's C++ scripts are here (most important method being Update, for what I'm doing, but it also has keyboard detection methods) and here (Approach, which interpolates up and down based on the velocity goal)

    It's working for the most part but there's a strange issue with the transform moving (as soon as it isn't in-air) slightly in the x or z directions each frame (x more often than z), like less than 0.000001, even when no key is pressed/the velocity goal is set to 0. This doesn't always happen though; it does often but sometimes the transform position stands still when the goal is 0.

    Gif of what I'm talking about.

    Maybe this is just because floats are involved and as I was told they never reach 0 exactly. Maybe I'm missing something in trying to "convert" from the Visual Studio C++ to Unity/VS/C+.

    Thanks for any help. Not a big deal but I'd like to figure this out before moving on.

    KeyboardControls
    Code (CSharp):
    1.  
    2. public class EulerKeyboardControls : MonoBehaviour {
    3.  
    4.     //the velocity goal
    5.     [HideInInspector]
    6.     public Vector3 vecVelocityGoal;
    7.  
    8.     //the fastest the player can move
    9.     public float topSpeedX = 10;
    10.     public float topSpeedZ = 10;
    11.  
    12.     // Update is called once per frame
    13.     void Update()
    14.     {
    15.         //if moving left
    16.         if (Input.GetAxisRaw("Horizontal") < 0)
    17.         {
    18.             //set the fastest possible speed as negative
    19.             vecVelocityGoal.x = topSpeedX * -1;
    20.         }
    21.         //else if moving right
    22.         if (Input.GetAxisRaw("Horizontal") > 0)
    23.         {
    24.             //set the fastest possible speed as positive
    25.             vecVelocityGoal.x = topSpeedX;
    26.         }
    27.      
    28.         //if moving down
    29.         if (Input.GetAxisRaw("Vertical") < 0)
    30.         {
    31.             //set the fastest possible speed as negative (down)
    32.             vecVelocityGoal.z = topSpeedZ * -1;
    33.         }
    34.  
    35.         //if moving up
    36.         if (Input.GetAxisRaw("Vertical") > 0)
    37.         {
    38.             //set the fastest possible speed as positive (up)
    39.             vecVelocityGoal.z = topSpeedZ;
    40.         }
    41.  
    42.         //if not moving left or right
    43.         if (Input.GetAxisRaw("Horizontal") == 0)
    44.         {
    45.             //set the fastest possible speed as unmoving
    46.             vecVelocityGoal.x = 0;
    47.         }
    48.  
    49.         //if not moving up or down
    50.         if (Input.GetAxisRaw("Vertical") == 0)
    51.         {
    52.             //set the fastest possible speed as unmoving
    53.             vecVelocityGoal.z = 0;
    54.         }
    55.     }
    56. }
    57.  
    SmoothMovementInterpolation
    Code (CSharp):
    1. public class EulerSmoothMovementInterpolation : MonoBehaviour {
    2.  
    3.     EulerKeyboardControls kc;
    4.     float gravityForY = -4;
    5.     Rigidbody rb;
    6.     Vector3 rbVel;
    7.  
    8.     //scale the deltaTime by this (to speed up or slow interpolation)
    9.     float dtScaler = 35;
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.         rb = GetComponent<Rigidbody>();
    14.         kc = GetComponent<EulerKeyboardControls>();
    15.     }
    16.  
    17.     //interpolate up or down to the goal speed
    18.     float Approach(float goal, float current, float dt)
    19.     {
    20.         //how much left do we have to go before we reach our goal
    21.         float diff = goal - current;
    22.  
    23.         //if dt is not enough to take us to our goal
    24.         if (diff > dt)
    25.         {
    26.             //return one step farther to our goal
    27.             return current + dt;
    28.         }
    29.  
    30.         //if interpolating in the downward (on the graph) direction (ie, player is slowing down) and the difference is not enough for us to get to our goal
    31.         if (diff < -dt)
    32.         {
    33.             //return one step farther to our goal, in the downward/slowing direction
    34.             return current - dt;
    35.         }
    36.  
    37.         //return goal if in range (previous returns will stop this return if need be)
    38.         return goal;
    39.     }
    40.  
    41.     // Update is called once per frame
    42.     void Update()
    43.     {
    44.         float dt = Time.deltaTime;
    45.         if (dt > 0.15f)
    46.             dt = 0.15f;
    47.  
    48.         //apply interpolation method and return x and z velocities and store in vars
    49.         rbVel.x = Approach(kc.vecVelocityGoal.x, rb.velocity.x, dt * dtScaler);
    50.         rbVel.z = Approach(kc.vecVelocityGoal.z, rb.velocity.z, dt * dtScaler);
    51.      
    52.         //update position based on previous position + amount supposed to change * dt (to slow down)
    53.         transform.position = new Vector3(transform.position.x + rbVel.x * dt, transform.position.y, transform.position.z + rbVel.z * dt);
    54.  
    55.         //update velocity based on the previous velocity
    56.         rb.velocity = new Vector3(rbVel.x, rb.velocity.y, rbVel.z);
    57.     }
    58. }
    59.  
     
    Last edited: Aug 29, 2019
  2. FlashMuller

    FlashMuller

    Joined:
    Sep 25, 2013
    Posts:
    449
    You should not combine transform.position and a rigidbody as it will break the physics and have a bad impact on your performance. You can use rb.position instead. See here: https://docs.unity3d.com/ScriptReference/Rigidbody-position.html

    One thing I could think off is that your rigidbody friction is too low to actually ever stop the GameObject. The most simple solution: If velocity is below a certain value force it to zero via code.

    Have you tested that rbVel actually becomes zero?
     
    tytbone likes this.
  3. tytbone

    tytbone

    Joined:
    Jul 16, 2013
    Posts:
    18
    Thanks for responding. Yeah, rbVel gets to 0.0 at least in the Debug.Log (but maybe I need to check the full float value with something like Debug.Log(point.ToString("F4"));). Weirdly enough, in the SmoothMovementInterpolation script, if I leave out everything from line 53 to the end, even if I just use a line like "rb.velocity = Vector3.zero" instead, I still have the same issue/the x position still goes down slowly. It's like setting the rb.velocity or rb.transform equal to anything causes the rb to move in tiny amounts.

    I may try contacting the guy who created the videos and see if he has any idea, or on Reddit. This is just me trying to learn, not anything serious nor for an actual game.
     
  4. ibbybn

    ibbybn

    Joined:
    Jan 6, 2017
    Posts:
    193
    Pretty sure debug.log rounds vector3 floats to 1 or 2 decimal places. Instead make a public vector3 pass the velocity every frame and watch it in the component or create a debug canvas showing you the most important values. Especially useful when creating your own character controller. There'll always be edge cases you'll have to check later on when expanding your characters abilities. ;)
     
    Last edited: Aug 30, 2019
    tytbone likes this.
  5. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    My guess is that this has nothing whatsoever to do with your code. Try disabling your code, and then put the cube into various positions by using the Unity transform tool on the scene view while the game is running. I bet you'll see similar results.

    Imagine your object was sitting on a slope. Every frame, it would try to fall straight downward, then collide with the slope, which would push it to a side, resulting in a net diagonal movement.

    An object sitting on a flat floor is still going through all of those same calculations; the only difference is the angle of the "slope" happens to be zero. Theoretically, those motions should cancel out and leave it at the same position, but because you're approximating a continuous physical movement using discrete time-steps and approximating continuous positions with floating-point values, you can't really count on them exactly canceling.

    Physics is dirty. If two physical objects are in contact with each other, they will probably never reach true equilibrium.

    As long as the drift can't be seen with the naked eye, you probably shouldn't worry about it.
     
    tytbone likes this.
  6. tytbone

    tytbone

    Joined:
    Jul 16, 2013
    Posts:
    18
    I think this worked too (I could see rbVel go up to 8+ decimal places when the velocity was > or < 0).

    Thanks, I tried that but it is the code. (Movement was extremely small obviously but over time the change would have been visible.) It seems constantly setting the rb.position or rb.velocity in Update usually made the x or z pos try to reach 0 but never completely (sort of how you were talking about the messiness of physics ;)

    I think I solved it by just saying "if the abs value of rbVel's (the velocity goal) magnitude is less than a really small number, don't even touch the code to set rb.position or rb.velocity"

    Code (CSharp):
    1. void Update()
    2.     {
    3.      
    4.         //store the time between frames to act as difference
    5.         float dt = Time.deltaTime;
    6.  
    7.         //clamp dt
    8.         if (dt > 0.15f)
    9.             dt = 0.15f;
    10.  
    11.         //apply interpolation method and return x and z velocities and store in vars
    12.         rbVel.x = Approach(kc.vecVelocityGoal.x, rb.velocity.x, dt * dtScaler);
    13.         rbVel.z = Approach(kc.vecVelocityGoal.z, rb.velocity.z, dt * dtScaler);
    14.  
    15. //if total velocity goal is greater than a really small amount, basically zero
    16.        [B] if (Mathf.Abs(rbVel.magnitude) > 0.00001)[/B]
    17.         {
    18.             //update position based on previous position + amount supposed to change * dt (to slow down)
    19.             rb.position = new Vector3(rb.position.x + rbVel.x * dt, rb.position.y, rb.position.z + rbVel.z * dt);
    20.  
    21.             //update velocity based on the previous velocity + gravity * dt (to slow down)
    22.             rb.velocity = new Vector3(rbVel.x, rb.velocity.y + gravityForY * dt, rbVel.z);
    23.         }
    24.     }
    Again this is all just practice, I won't use this for a real game. I just kinda wish I knew why the C++ code version seemingly didn't need something like this. (Maybe he just wanted to keep things simple for the video) I may still try contacting him.

    Thanks again all.
     
    Last edited: Aug 30, 2019
  7. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    FYI Unity provides a function called Mathf.Approximately that you might find convenient for the "ignore really tiny differences" approach.
     
    CurtisMcGill and tytbone like this.
  8. unity_oVsJgtACmNlH5w

    unity_oVsJgtACmNlH5w

    Joined:
    Aug 2, 2020
    Posts:
    4
    In my case it was the friction. I needed to add player material with 0 friction, that would stop the minimal velocity