Search Unity

Resolved Rigidbody velocity on y axis triggering wrong animation

Discussion in 'Scripting' started by stefaxel, Jan 26, 2023.

  1. stefaxel

    stefaxel

    Joined:
    Oct 8, 2021
    Posts:
    47
    I'm using an enum to trigger different animations on a 2D project, and to trigger a falling animation I'm using the velocity on the y axis. However at run time there always seems to be a small velocity on the y axis, as the falling animation is playing even though the player isn't falling.

    I'm using a method to apply a falling force for when the player is falling and a jump force dependent on how long the space key is pressed. I'm also applying acceleration and deceleration on the player characters movement.

    Code (CSharp):
    1. private enum AnimationState { idle, running, jumping, falling }
    2.     private AnimationState state;
    3.  
    4. private void Update()
    5.     {
    6.         AppyCoyoteTime();
    7.  
    8.         ApplyFlip();
    9.  
    10.         ApplyGravityForces();
    11.  
    12.         UpdateAnimation();
    13.     }
    14.  
    15. private void ApplyGravityForces()
    16.     {
    17.         // Applying gravity depending on if the player is falling or pressing and holding the jump key
    18.         if (rb.velocity.y < 0 && !wallJump)
    19.         {
    20.             rb.velocity += Vector2.up * Physics2D.gravity.y * (fallRate - 1) * Time.deltaTime;
    21.         }
    22.         if (rb.velocity.y > 0 && !jumpAction.IsPressed() && !wallJump)
    23.         {
    24.             rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpRate - 1) * Time.deltaTime;
    25.         }
    26.     }
    27.  
    28. private void UpdateAnimation()
    29.     {
    30.         // Triggers which animation to play
    31.         if (movementInputX.x > 0f)
    32.         {
    33.             state = AnimationState.running;
    34.         }
    35.         else if (movementInputX.x < 0f)
    36.         {
    37.             state = AnimationState.running;
    38.         }
    39.         else
    40.             state = AnimationState.idle;
    41.  
    42.         if (rb.velocity.y > 0.1f)
    43.         {
    44.             state = AnimationState.jumping;
    45.         }
    46.         else if (rb.velocity.y < -0.1f)
    47.         {
    48.             state = AnimationState.falling;
    49.         }
    50.  
    51.         Debug.Log(rb.velocity.y);
    52.  
    53.         playerAnimation.SetInteger("state", (int)state);
    54.     }
    55.  
    56. private void CalcAccelerationAndDeceleration()
    57.     {
    58.         // Calaculates acceleration and deceleration
    59.         rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxSpeed);
    60.  
    61.         float force = movementInputX.x * maxSpeed * accelerationSpeed * Time.deltaTime;
    62.         if (Mathf.Abs(movementInputX.x) < 0.1f)
    63.         {
    64.             force = -rb.velocity.x * decelerationSpeed;
    65.         }
    66.         rb.AddForce(new Vector2(force, 0));
    67.        
    68.         // Use different value for wall climb acceleration
    69.         if (wallGrab && wallClimbAction.IsPressed() && IsOnWall())
    70.         {
    71.             if (moveAction.IsPressed())
    72.             {
    73.                 rb.velocity = new Vector2(0, movementInputY.y);
    74.                 rb.velocity = Vector2.ClampMagnitude(rb.velocity, climbSpeed);
    75.                 float forceUp = movementInputY.y * climbSpeed * Time.deltaTime;
    76.                 rb.AddForce(new Vector2(0, forceUp));
    77.             }
    78.             if (!moveAction.IsPressed())
    79.             {
    80.                 rb.gravityScale = 0;
    81.                 rb.velocity = Vector2.zero;
    82.             }
    83.             if (jumpAction.IsPressed() && coyoteTimeCounter > 0f)
    84.             {
    85.                 rb.AddForce(Vector2.up * jumpForce);
    86.             }
    87.         }
    88.     }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Welcome to floating point!!

    "Think of [floating point] as JPEG of numbers." - orionsyndrome on the Unity3D Forums

    The solution is to use a small "guardband" amount (rather than zero) that you consider "close enough to zero."

    Here's why:

    Floating (float) point imprecision:

    Never test floating point (float) quantities for equality / inequality. Here's why:

    https://starmanta.gitbooks.io/unitytipsredux/content/floating-point.html

    https://forum.unity.com/threads/debug-log-2000-0f-000-1f-is-200.1153397/#post-7399994

    https://forum.unity.com/threads/why-doesnt-this-code-work.1120498/#post-7208431
     
    stefaxel likes this.
  3. stefaxel

    stefaxel

    Joined:
    Oct 8, 2021
    Posts:
    47
    I swapped out 0 with Mathf.Epsilon:

    https://docs.unity3d.com/ScriptReference/Mathf.Epsilon.html

    So the UpdateAnimation and ApplyGravityForces methods look like this:
    Code (CSharp):
    1. private void ApplyGravityForces()
    2.     {
    3.         // Applying gravity depending on if the player is falling or pressing and holding the jump key
    4.         if (rb.velocity.y < -Mathf.Epsilon && !wallJump)
    5.         {
    6.             rb.velocity += Vector2.up * Physics2D.gravity.y * (fallRate - 1) * Time.deltaTime;
    7.         }
    8.         if (rb.velocity.y > 0 && !jumpAction.IsPressed() && !wallJump)
    9.         {
    10.             rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpRate - 1) * Time.deltaTime;
    11.         }
    12.     }
    13.  
    14.     private void UpdateAnimation()
    15.     {
    16.         // Triggers which animation to play
    17.         if (movementInputX.x > 0)
    18.         {
    19.             state = AnimationState.running;
    20.         }
    21.         else if (movementInputX.x < 0)
    22.         {
    23.             state = AnimationState.running;
    24.         }
    25.         else
    26.             state = AnimationState.idle;
    27.  
    28.         if (rb.velocity.y > 0)
    29.         {
    30.             state = AnimationState.jumping;
    31.         }
    32.         else if (rb.velocity.y < -Mathf.Epsilon)
    33.         {
    34.             state = AnimationState.falling;
    35.         }
    36.  
    37.         Debug.Log(rb.velocity.y);
    38.  
    39.         playerAnimation.SetInteger("state", (int)state);
    40.     }
    However I'm still getting the wrong animation being triggered.

    This is the Debug.Log velocity on the y-axis even though the player character is grounded there is still a downward velocity. If the jump key is pressed the jump animation is played, then the fall animation but it doesn't go back to idle or the running animation.
    View attachment 1187383
    Here the player character is moving left, as it accelerates the animation changes from running to falling
    View attachment 1187380
     

    Attached Files:

  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Oh that's essentially zero..

    Try something like

    Code (csharp):
    1. if (velocity.y < -0.1f)
    2. {
    3.   // falling
    4. }
    5. if (velocity.y > +0.1f)
    6. {
    7.   // rising
    8. }
    Substitute 0.1f for even 0.5f, 1.0f or 2.0f or ... more

    One of those will feel "good."

    Push forward, don't get hung up on zero.
     
    stefaxel likes this.
  5. stefaxel

    stefaxel

    Joined:
    Oct 8, 2021
    Posts:
    47
    Thanks, my bad. I have used -1.0f, and it works great.
     
    Kurt-Dekker likes this.