Some issues with my movement script

Hello!
I seem to run into a couple of issues with my script here, any tips would be appreciated!

  1. Character still seems to clamp near the top of a wall while jumping and is able to climb onto the object.
  2. While moving and jumping, if character hits a platform with his head, the Y movement is stopped and instantly falls. However, when the character is jumping while standing still, he still sticks to the ceiling for a brief second.
  3. Character is able to un-crouch under a platform, what would be a decent solution to this?
  4. I am still in the earlier stages of learning, any tips on to clean up my code are also helpful
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMove : MonoBehaviour
{
    [SerializeField] private float walkSpeed, runSpeed, sprintSpeed;
    [SerializeField] private float runBuildUpSpeed;
    [SerializeField] private float originalHeight;
    [SerializeField] private float crouchHeight = 0.5f;

    private float movementSpeed;
   

    [SerializeField] private float slopeForce;
    [SerializeField] private float slopeForceRayLength;

    private CharacterController charController;
    [SerializeField] private AnimationCurve jumpFallOff;
    [SerializeField] private float jumpMultiplier;

    private bool isJumping = false;
    private bool isSprinting = false;
    private bool isWalking = false;
    private bool isCrouching = false;

    [SerializeField] KeyCode jumpKey;
    [SerializeField] KeyCode sprintKey;
    [SerializeField] KeyCode walkKey;
    [SerializeField] KeyCode crouchKey;
   

    // Start is called before the first frame update
    private void Start()
    {
        charController = GetComponent<CharacterController>();
        originalHeight = charController.height;
    }

    // Update is called once per frame
    private void Update()
    {
        PlayerMovement();
    }

    private void PlayerMovement()
    {
        // Check movement and speed
        float xInput = Input.GetAxis("Horizontal");
        float zInput = Input.GetAxis("Vertical");
       
        Vector3 forwardMovement = transform.forward * zInput;
        Vector3 rightMovement = transform.right * xInput;

        charController.SimpleMove(Vector3.ClampMagnitude(forwardMovement + rightMovement, 1.0f) * movementSpeed);

        //Clamp to slopes
        if((xInput != 0 || zInput !=0) && onSlope())
        {
            charController.Move(Vector3.down * charController.height / 2 * slopeForce * Time.deltaTime);
        }

        SetMovementSpeed();
        JumpInput();
    }

    private void SetMovementSpeed()
    {
        SprintInput();
        WalkInput();
        CrouchInput();
    }

    private void SprintInput()
    {
        if(Input.GetKey(sprintKey) && !isWalking && !isCrouching && Input.GetKey(KeyCode.W))
        {
            isSprinting = true;
            movementSpeed = Mathf.Lerp(movementSpeed, sprintSpeed, Time.deltaTime * runBuildUpSpeed);
        }
           
        else
        {
            isSprinting = false;
            movementSpeed = Mathf.Lerp(movementSpeed, runSpeed, Time.deltaTime * runBuildUpSpeed); 
        }
                 
    }

    private void WalkInput()
    {
        if(Input.GetKey(walkKey) && !isSprinting)
        {
            isWalking = true;
            movementSpeed = Mathf.Lerp(movementSpeed, walkSpeed, Time.deltaTime * 100);
        }
        else
        {
            isWalking = false;
            movementSpeed = Mathf.Lerp(movementSpeed, runSpeed, Time.deltaTime * runBuildUpSpeed); 
        }
           
    }

    private void CrouchInput()
    {
        if(Input.GetKey(crouchKey))
        {
            isCrouching = true;
            charController.height = crouchHeight;
        }
        else
        {
            isCrouching = false;
            charController.height = Mathf.Lerp(charController.height, originalHeight, Time.deltaTime * 30);
        }
    }

    private bool onSlope()
    {
        if (isJumping)
            return false;
       
            RaycastHit hit;

            if(Physics.Raycast(transform.position, Vector3.down, out hit, charController.height / 2 * slopeForceRayLength))
                if(hit.normal != Vector3.up)
                    return true;
        return false;
           
    }

    private void JumpInput()
    {
        if(Input.GetKeyDown(jumpKey) && !isJumping)
        {
            isJumping = true;
            StartCoroutine(JumpEvent());
        }
    }

    private IEnumerator JumpEvent()
    {

        charController.slopeLimit = 90.0f;

        float timeInAir = 0.0f;

        do
        {
            float jumpForce = jumpFallOff.Evaluate(timeInAir);
            charController.Move(Vector3.up * jumpForce * jumpMultiplier * Time.deltaTime);
            timeInAir += Time.deltaTime;

            yield return null;
        } while (!charController.isGrounded && charController.collisionFlags != CollisionFlags.Above);
       
        charController.slopeLimit = 45.0f;
        isJumping = false;
    }
}

For #1 and #2, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run?
  • what are the values of the variables involved? Are they initialized?

Knowing this information will help you reason about the behavior you are seeing.

For #3 you can make a “no stand” trigger zone under things you can crouch under. Or else do a raycast up and see if there is room. The general-case problem can be finicky to solve, such as being in an area that is ALMOST high enough to allow standing by not numerically high enough.

For #4 it doesn’t matter how clean your code is. If you understand 100% of what is going on, you will develop a sense for which parts are awkward and begin to look for ways to refactor them.

Just eyeballing it, the first thing I would refactor is the lines where you check many different conditions all in one if statement. Those are virtually impossible to reason about, but if they work, hey, leave them alone. Just realize if you come back to change them, it might be easy to break some behavior unwittingly.