How can I apply counter-movement instead of changing Rigidbody drag?

In my Rigidbody FPS controller script, I change the drag in the Rigidbody to prevent the player from going super fast. However, this has led to some problems such as me needing to add a constant down force when in the air to prevent floating down super slowly. I don’t want to change the drag value in my Rigidbody so that leads me to the question, how can I add counter-movement into my game?

Here is the script

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

public class PlayerMovement : MonoBehaviour
{
    public static PlayerMovement instance;

    float playerHeight = 2f;

    [SerializeField] Transform orientation;
    [SerializeField] float moveSpeed = 6f;
    [SerializeField] float airMultiplier = 0.4f;
    float movementMultiplier = 10f;

    [Header("Sprinting")]
    [SerializeField] float walkSpeed = 4f;
    [SerializeField] float sprintSpeed = 7f;

    float crouchSpeedMod = 0.5f;
    float shootSpeedMod = 0.8f;
    float aimSpeedMod = 0.5f;
    [SerializeField] float acceleration = 10f;

    public bool isShooting;
    public bool isAiming;
    public bool isReloading;
    public bool isSprinting;
    public bool isCrouching;

    [Header("Jumping")]
    public float jumpForce = 5f;
    public float downForce = 1f;

    [Header("Keybinds")]
    [SerializeField] KeyCode jumpKey = KeyCode.Space;
    [SerializeField] KeyCode sprintKey = KeyCode.LeftShift;
    [SerializeField] KeyCode slideKey = KeyCode.LeftControl;

    [Header("Drag")]
    [SerializeField] float groundDrag = 6f;
    [SerializeField] float airDrag = 2f;

    float horizontalMovement;
    float verticalMovement;

    [Header("Ground Detection")]
    [SerializeField] Transform groundCheck;
    [SerializeField] LayerMask groundMask;
    [SerializeField] float groundDistance = 0.2f;
    public bool isGrounded { get; private set; }

    [Header("Sliding")]
    public float slideForce = 25f;
    public GameObject player;
    Vector3 slide = new Vector3(1f, 0.5f, 1f);

    public float shrinkSpeed = 3f;
    public float slideScale = 0.75f, originalScale = 1f;
    private float targetScale = 1f;

    public float slideCooldown;
    private float currentSlideCooldown;

    Vector3 moveDirection;
    Vector3 slopeMoveDirection;

    Rigidbody rb;

    RaycastHit slopeHit;

    [Header("Speed Calc")]
    Vector3 previousFramePos = Vector3.zero;
    float currentSpeed = 0f;
    public TMP_Text speedText;

    [Header("Animation")]
    public Animator anim;


    private bool OnSlope()
    {
        if (Physics.Raycast(transform.position, Vector3.down, out slopeHit, playerHeight / 2 + 0.5f))
        {
            if (slopeHit.normal != Vector3.up)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        return false;
    }

    private void Awake()
    {
        instance = this;
    }
    private void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.freezeRotation = true;
    }

    void Update()
    {
        transform.localScale = Vector3.Lerp(transform.localScale, new Vector3(0, targetScale), shrinkSpeed * Time.deltaTime);


        if (currentSlideCooldown > 0f)
        {
            currentSlideCooldown -= Time.deltaTime;
        }
        else
        {
            if (Input.GetKeyDown(slideKey) && Input.GetKey(KeyCode.W))
            {
                StartSlide();
                Crouch();
                currentSlideCooldown = slideCooldown;
            }
            else if (Input.GetKeyUp(slideKey))
            {
                StopSlide();
            }
        }


        if (Input.GetKeyDown(slideKey))
        {
            Crouch();
        }
        else if (Input.GetKeyUp(slideKey))
        {
            StopSlide();
        }

        isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);

        MoveInput();
        ControlDrag();
        ControlSpeed();
        ControlAnimations();



        slopeMoveDirection = Vector3.ProjectOnPlane(moveDirection, slopeHit.normal);

        float movementPerFrame = Vector3.Distance(new Vector3(previousFramePos.x, 0f, previousFramePos.z), new Vector3(transform.position.x, 0f, transform.position.z));
        currentSpeed = movementPerFrame / Time.deltaTime;
        previousFramePos =  new Vector3(transform.position.x, 0f, transform.position.z);


        speedText.text = "Speed: " + currentSpeed;

        Debug.Log(rb.velocity.magnitude.ToString());
    }

    private void FixedUpdate()
    {
        MovePlayer();

        if (!isGrounded)
            rb.AddForce(Vector3.down * downForce, ForceMode.Force);

        if (Input.GetKey(jumpKey) && isGrounded)
        {
            Jump();
            StopSlide();
        }
    }

    void MoveInput()
    {
        horizontalMovement = Input.GetAxisRaw("Horizontal");
        verticalMovement = Input.GetAxisRaw("Vertical");

        moveDirection = orientation.forward * verticalMovement + orientation.right * horizontalMovement;

    }

    void Jump()
    {
        if (isGrounded)
        {
            rb.velocity = new Vector3(rb.velocity.x, 0, rb.velocity.z);
            rb.AddForce(transform.up * jumpForce, ForceMode.Impulse);
        }
    }

    void ControlSpeed()
    {

        if (Input.GetKey(sprintKey) && isGrounded)
        {
            isSprinting = true;
        }
        else if (!Input.GetKey(sprintKey))
        {
            isSprinting = false;
        }

        if (Input.GetKey(slideKey) && isGrounded)
        {
            isCrouching = true;
        }
        else
        {
            isCrouching = false;
        }

        if (isShooting || isAiming || isReloading || isCrouching)
        {
            isSprinting = false;
        }


        if (isSprinting)
        {
            moveSpeed = Mathf.Lerp(moveSpeed, sprintSpeed, acceleration * Time.deltaTime);
        }
        else
        {
            if (isCrouching)
            {
                if (isAiming)
                {
                    if (isShooting)
                        moveSpeed = Mathf.Lerp(moveSpeed, walkSpeed * shootSpeedMod * aimSpeedMod * crouchSpeedMod, acceleration * Time.deltaTime);
                    else
                    {
                        moveSpeed = Mathf.Lerp(moveSpeed, walkSpeed * aimSpeedMod * crouchSpeedMod, acceleration * Time.deltaTime);
                    }
                }
                else
                {
                    if (isShooting)
                        moveSpeed = Mathf.Lerp(moveSpeed, walkSpeed * shootSpeedMod * crouchSpeedMod, acceleration * Time.deltaTime);
                    else
                    {
                        moveSpeed = Mathf.Lerp(moveSpeed, walkSpeed * crouchSpeedMod, acceleration * Time.deltaTime);
                    }
                }
            }
            else
            {

                if (isAiming)
                {
                    if (isShooting)
                        moveSpeed = Mathf.Lerp(moveSpeed, walkSpeed * shootSpeedMod * aimSpeedMod, acceleration * Time.deltaTime);
                    else
                    {
                        moveSpeed = Mathf.Lerp(moveSpeed, walkSpeed * aimSpeedMod, acceleration * Time.deltaTime);
                    }
                }
                else
                {
                    if (isShooting)
                        moveSpeed = Mathf.Lerp(moveSpeed, walkSpeed * shootSpeedMod, acceleration * Time.deltaTime);
                    else
                    {
                        moveSpeed = Mathf.Lerp(moveSpeed, walkSpeed, acceleration * Time.deltaTime);
                    }
                }
            }
        }
    }

    void ControlAnimations()
    {
        if (isShooting)
        {
            anim.SetBool("isWalking", false);
            anim.SetBool("isSprinting", false);
            return;
        }

        if (!isGrounded)
        {
            anim.SetBool("isSprinting", false);
            anim.SetBool("isWalking", false);
            isSprinting = false;
            return;
        }

        if (horizontalMovement != 0f || verticalMovement != 0f)
        {
            if (isSprinting)
            {
                anim.SetBool("isSprinting", true);
                anim.SetBool("isWalking", false);
                return;
            }
            else
            {
                anim.SetBool("isWalking", true);
                anim.SetBool("isSprinting", false);
                return;
            }
        }
        else
        {
            anim.SetBool("isSprinting", false);
            anim.SetBool("isWalking", false);
            return;
        }

    }

    void ControlDrag()
    {
        if (isGrounded)
        {
            rb.drag = groundDrag;
        }
        else
        {
            rb.drag = airDrag;
        }
    }

    void MovePlayer()
    {
        if (isGrounded && !OnSlope())
        {
            rb.AddForce(moveDirection * moveSpeed * movementMultiplier, ForceMode.Acceleration);
        }
        else if (isGrounded && OnSlope())
        {
            rb.AddForce(slopeMoveDirection * moveSpeed * movementMultiplier, ForceMode.Acceleration);
        }
        else if (!isGrounded)
        {
            rb.AddForce(moveDirection * moveSpeed * movementMultiplier * airMultiplier, ForceMode.Acceleration);
        }
    }

    void Crouch()
    {
        targetScale = slideScale;
    }

    void StartSlide()
    {
        if(rb.velocity.magnitude > 0.5f)
            rb.AddForce(orientation.forward * slideForce);
    }

    void StopSlide()
    {
        targetScale = originalScale;
    }
}

Hello,

I see you’re using velocity. Maybe something like

if (rb.velocity <= 100f) // or some other value in a public or serialized variable to let you adjust it
{
   // Apply forces or velocities
}

would do the trick just fine.

I think you meant “rb.velocity.magnitude” because it’s a Vector3 not a float but your advice is otherwise correct.

1 Like

Absolutely. Careless mistake :slight_smile:

1 Like

Thanks guys for your help, I actually went around a few discords and found an answer.

Here is what I did incase anyone is wondering:

void CounterMovement()
    {
        Vector3 vel = rb.velocity;
        vel.y = 0f;

        float coefficientOfFriction = (moveSpeed * movementMultiplier) / currentMaxSpeed;

        rb.AddForce(-vel * coefficientOfFriction, ForceMode.Acceleration);
    }