Need help with controlling x/z velocity while falling

Hi, new to Unity, new to coding here. I’m sure I will leave out pertinent information.

Problem: I don’t want the player to be able to change x or z velocities while jumping or falling. Rather, I want the player to continue with the last x/z velocity inputs while isGrounded. I am using a Character Controller for movement.

Attempted solution: I thought maybe I could update a new Vector3 fallingMove from Vector3 move while player isGrounded. While player !isGrounded, call controller.Move() using fallingMove as parameter instead of move.

Outcome: x and z velocities are 0 while falling/jumping. In essence, player falls straight down.

Thank you in advance for any help or advice. My movement script is below. My attempted solution is inside PlayerMove().

using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerMovement : MonoBehaviour
{
    public InputMaster controls;
  
    public CharacterController controller;

    public Transform groundCheck;
    public float groundDistance = 0.4f;
    public LayerMask groundMask;

    public float groundSpeed = 12;
    public float gravity = -9.81f;
    public float jumpHeight = 3;

    bool cameraGrabbed;
    bool playerGrabbed;
    bool isAutoRunning;
    bool isGrounded;
  
    Vector2 cursorPosition;

    Vector2 movementInput;

    Vector3 fallingVelocity;
    Vector3 move;
    Vector3 fallingMove;


    void Awake() {
        controls = new InputMaster();

        // started called on button press
        controls.Player.GrabCamera.started += _ => GrabCamera();
        controls.Player.GrabPlayer.started += _ => GrabPlayer();
      
        // performed called on button release
        controls.Player.GrabCamera.performed += _ => ReleaseCamera();
        controls.Player.GrabPlayer.performed += _ => ReleasePlayer();

        controls.Player.Jump.started += _ => Jump();
        controls.Player.AutoRun.started += _ => AutoRun();
        controls.Player.AutoRunCancel.started += _ => AutoRunCancel();


    }  

    void Start() {
        //Cursor.lockState = CursorLockMode.Confined;
    }

    void GrabCamera() {
        cameraGrabbed = true;
        if (playerGrabbed) {
            AutoRunCancel();
        }
    }

    void GrabPlayer() {
        playerGrabbed = true;
        if (cameraGrabbed) {
            AutoRunCancel();
        }
    }
  
    void ReleaseCamera() {
        cameraGrabbed = false;
    }

    void ReleasePlayer() {
        playerGrabbed = false;
    }

    // Update is called once per frame
    void Update() {
        PlayerMove();
        FreeFall();
    }
  
    void AutoRun() {
        if (isAutoRunning == false) {
            isAutoRunning = true;
        }
        else {
            isAutoRunning = false;
        }
    }

    void AutoRunCancel() {
            isAutoRunning = false;
    }

    void PlayerMove() {
        Vector2 movementInput = controls.Player.KeyboardMovement.ReadValue<Vector2>();
        if (cameraGrabbed == true && playerGrabbed == true) {
            movementInput.y = 1;
            }
        if (isAutoRunning == true) {
            movementInput.y = 1;
            }

        movementInput = movementInput.normalized;

        float moveX = movementInput.x;
        float moveZ = movementInput.y;
      
        if (isGrounded) {
            Vector3 move = transform.right * moveX + transform.forward * moveZ;
            Vector3 fallingMove = move;
            controller.Move(move * groundSpeed * Time.deltaTime);
        }

        else {
            controller.Move(fallingMove * groundSpeed * Time.deltaTime);
        }
    }

    void Jump() {
        if (isGrounded) {
            fallingVelocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
        }
      
    }

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

        if (isGrounded && fallingVelocity.y < 0) {
            fallingVelocity.y = -2f;
        }
        fallingVelocity.y += gravity * Time.deltaTime;
        controller.Move(fallingVelocity * Time.deltaTime);
        }

    void OnEnable() {
        controls.Enable();
    }

    void onDisable() {
        controls.Disable();
    }
}

On line 110, you’re declaring a new scoped variable also called fallingMove, which is hiding the fallingMove you declared on the class. Therefore, the class’ fallingMove field is never being assigned to.

1 Like

Madgvox, what an EYE! Nailed it…

Hey OP, what Mad means is to remove the Vector3 portion of the above. The presence of Vector3 makes the fresh local copy, “hiding” the real one you want to change.

1 Like

@Madgvox that did solve my problem. Thank you!

@Kurt-Dekker thank you for helping me understand what I needed to do.

I need to learn more about declaring variables in C#. My only programming experience is in Python and very limited at that.

They’re quite different. In Python a variable name is more like a sticky label you can slap on just any datatype you want at all, whereas in C# the type matters. An integer can only ever be an integer.

Either way, both languages support notions of scope, which is to say “how far something can be seen,” whether that’s a variable or a function, if it is global or local, etc. There’s a lot of detail, but I think it’s better to just sorta jump in and experiment, pick it up as you go.

ALSO: the specific error you have above is not even remotely a “noobie” error… I would say an inadvertently-local declared variable is a common enough typo that I commit it once a month or so. It can happen just by reflexively saying the type of the variable in your mind as you access it and then typing that type in.

The key is like any other error is to rapidly identify possible issues (again, just experience) and home in on it and fix it and move along. In this case I’m gonna bet that Mad has seen this more than once and it just snagged their eyeballs.

1 Like

@Kurt-Dekker thank you for your encouragement and help.

1 Like