I Lose Control Of My Rigidbody When Lerping My Speed

I’m trying to create a boost in speed and then the speed goes back to its original value using lerp, but the way I’m modifying my speed is somehow causing me to lose control of my character for a second once the speed starts to ramp back down. Could someone take a look at my code and tell me what I’m doing wrong?

By Lose control of my speed I mean whichever way I’m moving at that point where my speed starts to go back down, I cant change directions for at least a second.(its like im on ice at that point)

these are the only two functions that deal with my speed and my Checks function which right now only checks to see if im grounded

    void Checks () // Standard checks
    {

        // START GROUND CHECK
        if (Physics.Raycast(transform.position, -transform.up, out normalHit, playerToNormalRay, groundMask)) // Checks to see if the player is grounded.
        {
            isGrounded = true;
        }
        else
        {
            isGrounded = false;
        }
        // END GROUND CHECK
    }

This function is just for basic input and movement.

    void Movement() // Players base movement
    {
       // Debug.Log(_moveDirection);
        _moveDirection = new Vector3(Input.GetAxisRaw("Horizontal") * moveSpeed, Input.GetAxis("Jump") * jumpForce, Input.GetAxisRaw("Vertical") * moveSpeed); // Vector3 for movement input.

        if (isGrounded) // Stops the player from moving while airborne
        {
            if (rb.velocity.magnitude < curSpeed /* moveSpeed */) // Stops the speed from multiplying out into neverland
           {
               rb.AddRelativeForce(_moveDirection); // Using AddRelativeForce to move based on the players forward       
           }

        }
    }

moveSpeed is a float set to 15
_moveDirection is a Vector3
rb is the Rigidbody.

Below is where I think my problem lies (in the lerps)

This function handles the speed boost with lerps to ramp the speed up and then back down.
The part that handles the speed ramps starts with the if statement with the speedChange bool in the () brackets.

 void PlayerMomemtum() // Players Speed Momemtum
    {


        playersYVelocity = rb.velocity.y; // Players Y velocity at all times.

        if (isGrounded && playersYVelocity < 0 && velocitySwitch) // Runs once when the player is grounded, falling, and when the check velocitySwitch is reset.
        {
            playersYContactVelocity = playersYVelocity; // Grabs the last instance of playersYVelocity before hitting the ground.
                                                        //    rb.drag = playersLiftOffDrag;
            speedChange = true;
            velocitySwitch = false;

        }
        else if (playersYVelocity > 0) // Resets velocityGrab when the Player is going upwards in world space.
        {
            velocitySwitch = true;

        }

        if (speedChange)
        {
            curSpeed = Mathf.Lerp(curSpeed, maxSpeed, Time.deltaTime);
            if (curSpeed > maxSpeed - 1)
            {
                speedChange = false;
            }
        }
        else if (speedChange == false)
        {
            curSpeed = Mathf.Lerp(curSpeed, minSpeed, Time.deltaTime);
        }

    }

playerYVelocity is a float.
curSpeed is the current speed
minSpeed is set to 15
maxSpeed is set to 25
All are floats

All functions are in Update

Let me know if I need to include more, any help is greatly appreciated.

For starters, your lerp is incorrect. Since you have moving targets, you should use Mathf.MoveTowards instead.

As for your actual issue: You’re using AddRelativeForce, which creates an accumulation of force over time. The reason you feel like you’re sliding on ice is because your new forces have to overcome the previous forces before your character starts moving in the other direction. If you want snappy Megaman style movement, set the velocity explicitly instead.

1 Like

Thank you, I added Mathf.MoveTowards and it did make my momement alittle smoother, but my main problem seemed to be with the following code

 if (isGrounded) // Stops the player from moving while airborne
        {
            if (rb.velocity.magnitude < curSpeed) // Stops the speed from multiplying out into neverland
           {
               rb.AddRelativeForce(_moveDirection); // Using AddRelativeForce to move based on the players forward     
           }
        }

The rb.velocity.magnitude would be more than my curSpeed when I was ramping my speed down to minSpeed.

So after toying with it for about an hour I said screw it and Decided to clamp my speed instead, Below is the finished code.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private Rigidbody rb; // Rigidbody of the Player Capsule.

    public float moveSpeed = 15f; // Input Move Speed of the player.
    public Vector3 _moveDirection; // Stores player input for movement.

    public float playerToNormalRay = 1.1f; // Distance Ray for keeping the same rotation as the normal below the player(while grounded or hitting air off a ramp)
    public float airToGroundCorrectionRay = 15f; // Distance Ray for rotating toward the ground when the player is airborne(not hitting anything with playerToNormalRay)
    public LayerMask groundMask; // Layer Mask for the ground/ramps

    private Vector3 newUp; // The players transform.up, based on the normal below the player(Used to keep the player have the same up as the normal below).

    public bool isGrounded; // checks to see if the player is grounded.
    RaycastHit normalHit; // used to store the objects normal below the player in the newUp vector3(Used to keep the player have the same up as the normal below).

    public float jumpForce = 50f;

    // PlayerMomentum
    float playersYVelocity; // Players Y Velocity at all times.
    float playersYContactVelocity; // The players Y velocity at point of contact with Ground Mask(when player isGrounded == true)
    public float minSpeed = 15f; // The minimum speed of the player
    public float curSpeed; // The current speed of the player
    public float maxSpeed = 25f; // The Maximum speed of the player
    public bool velocitySwitch = false; // Used to reset playersYContactVelocity when the player starts going upward in world space.
    public float speedIncrement = 1.0f;
    public float speedDecrement = 1.0f;
    public bool speedChange = false;

    //DEBUG VARIABLES BELOW

   

    // Use this for initialization
    void Start()
    {
        rb = gameObject.GetComponent<Rigidbody>(); // Stores the Players Rigidbody in the rb variable

        curSpeed = minSpeed; // Sets the current speed to be at the minimum amount of speed posiable at the beginning of the game.
    }

    // Update is called once per frame
    void Update()
    {
        Checks(); // Standard checks

        Movement();  // players base movement

        PlayerRotation(); // Rotation of the player on ramps

        PlayerMomemtum(); // Players Speed Momemtum

        //DEBUGING BELOW

            // RAYCASTING
       
        Debug.DrawRay(transform.position, transform.up * 5, Color.green); // Used just as a visual to see which way the player is rotated.
        Debug.DrawRay(transform.position, -transform.up * playerToNormalRay, Color.red); // testing for the red up close normal ray.
        Debug.DrawRay(transform.position, Vector3.down * airToGroundCorrectionRay, Color.blue); // testing for the blue ground correction ray

           // END RAYCASTING

    }
   
    void Checks () // Standard checks
    {

        // START GROUND CHECK
        if (Physics.Raycast(transform.position, -transform.up, out normalHit, playerToNormalRay, groundMask)) // Checks to see if the player is grounded.
        {
            isGrounded = true;
        }
        else
        {
            isGrounded = false;
        }
        // END GROUND CHECK
    }

    void Movement() // Players base movement
    {
        _moveDirection = new Vector3(Input.GetAxisRaw("Horizontal") * moveSpeed, Input.GetAxis("Jump") * jumpForce, Input.GetAxisRaw("Vertical") * moveSpeed); // Vector3 for movement input.

        if (isGrounded) // Stops the player from moving while airborne
        {
            rb.velocity = Vector3.ClampMagnitude(rb.velocity, curSpeed); // Keeps the speed of the player from multiplying off into neverland. *curSpeed is determined in the PlayerMomentum Function*
            rb.AddRelativeForce(_moveDirection); // Using AddRelativeForce to move based on the players forward  
        }
    }

    void PlayerRotation() // Rotation of the player Capsule.
    {

       if (Physics.Raycast(transform.position, -transform.up, out normalHit, playerToNormalRay)) // Rotates the player while on ground, ramps, and on false air. (RED Ray)
        {
            newUp = normalHit.normal; // Putting the hit.normal in the vector3 newUp to make the players rotation the same as the below objects normal direction.
        } else if(Physics.Raycast(transform.position, Vector3.down, out normalHit, airToGroundCorrectionRay, groundMask)) // Rotates the player toward the rotation of the ramp or ground below when the player is completely airborne(aka no ground, ramp or false air just below the player). (BLUE Ray)
        {    
            newUp = normalHit.normal; // Putting the hit.normal in the vector3 newUp to make the players rotation the same as the below objects normal direction.
        }

        transform.up = newUp; // Setting the up position of the character based on the newUp vector3.
       
    }

    void PlayerMomemtum() // Players Speed Momemtum
    {

        playersYVelocity = rb.velocity.y; // Players Y velocity at all times.

        if (isGrounded && playersYVelocity < 0 && velocitySwitch) // Runs once when the player is grounded, falling, and when the check velocitySwitch is reset.
        {
            playersYContactVelocity = playersYVelocity; // Grabs the last instance of playersYVelocity before hitting the ground.
            speedChange = true; // Starts ramping the speed up to maxSpeed once the player hits Ground.
            velocitySwitch = false; // Causes this if statement to run 1 time once the player hits Ground

        }
        else if (playersYVelocity > 0) // Resets velocitySwitch when the Player is going upwards in world space.
        {
            velocitySwitch = true;

        }

        if (speedChange) // Starts ramping the speed up to maxSpeed once the player hits Ground.
        {
            curSpeed = Mathf.MoveTowards(curSpeed, maxSpeed, Time.deltaTime * speedIncrement);
            if (curSpeed > maxSpeed - 1) // Resets the speedChange when the curSpeed hits maxSpeed
            {
                speedChange = false;
            }
        }
        else if (speedChange == false) // Runs after the Speed has ramped up to maxSpeed
        {
            curSpeed = Mathf.MoveTowards(curSpeed, minSpeed, Time.deltaTime * speedIncrement); // Ramps the speed back down to minSpeed
        }

    }

}

Sorry for all the comments… I go overboard on comments…

Thanks for your help!

1 Like

How would you make the movement more snappy, like for example if you would ramp it up to max speed and then instantly set the speed to zero if stop moving ?