Jump back to the top

FPS Movement for Unity with CharacterController

31-01-2022

3 minutes to read

language: en


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

[RequireComponent(typeof(CharacterController))]

public class FPSPlayer : MonoBehaviour
{
    public float crouchVelocity = 2f;
    public float walkingVelocity = 5f;
    public float runningVelocity = 8f;
    public float slideForce = 6.0f;

    public float jumpHeight = 1.5f;
    public float gravity = -40.0f;
    public float mouseSensitivity = 3.0f;

    public LayerMask environmentMask;

    CharacterController characterController;
    public Transform playerCameraContainer;
    public Transform spaceCheck;

    Vector3 velocity = Vector3.zero;
    float slideVelocity = 0;

    Vector3 direction = Vector3.zero;
    Vector3 currentDirection = Vector3.zero;
    Vector3 smoothInputVelocity = Vector3.zero;
    float smoothDirectionSpeed = 0.1f;

    float smoothCrouchValue = 0.0f;
    float currentCrouchValue = 0.0f;
    float crouchValue = 0.5f;

    float xRotation = 0;
    float maxVelocity = 0;
    float friction = 10;

    bool isSliding = false;
    bool isCrouching = false;
    bool jumpingPossible = true;

    void Start()
    {
        characterController = GetComponent();

        // Lock cursor
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }

    void Update()
    {
        // calculate velocity
        valueUpdate();

        float x = Input.GetAxisRaw("Vertical");
        float z = Input.GetAxisRaw("Horizontal");

        if (!isSliding)
        {
            direction = transform.forward * x + transform.right * z;
        }
        direction.Normalize();
        currentDirection = Vector3.SmoothDamp(currentDirection, direction, ref smoothInputVelocity, smoothDirectionSpeed);

        velocity = new Vector3(currentDirection.x * (maxVelocity + slideVelocity), velocity.y, currentDirection.z * (maxVelocity + slideVelocity));

        float movementDirectionY = velocity.y;

        if (Input.GetButtonDown("Jump") && characterController.isGrounded && jumpingPossible)
        {
            velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
            isSliding = false;
            slideVelocity = 0;
        }
        else
        {
            velocity.y = movementDirectionY;
        }

        if (!characterController.isGrounded)
        {
            velocity.y += gravity * Time.deltaTime;
        }

        // Move the controller
        characterController.Move(velocity * Time.deltaTime);

        // Player and Camera rotation
        MouseLook();
    }

    public void MouseLook()
    {
        float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * 100 * Time.deltaTime;
        float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * 100 * Time.deltaTime;

        xRotation -= mouseY;
        xRotation = Mathf.Clamp(xRotation, -90.0f, 90.0f);

        playerCameraContainer.transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
        transform.rotation *= Quaternion.Euler(0, mouseX, 0);
    }

    public void valueUpdate()
    {
        // handling sliding, running and crouching
        if (characterController.isGrounded)
        {
            maxVelocity = walkingVelocity;
        }
        upperCharacter();

        if (Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.LeftControl) && !isCrouching && !isSliding && characterController.isGrounded)
        {
            slideVelocity = slideForce;
            isSliding = true;
            jumpingPossible = false;
        }
        if (slideVelocity < -0.5f)
        {
            jumpingPossible = true;
        }
        if (slideVelocity > -5.0f && isSliding)
        {
            slideVelocity -= friction * Time.deltaTime;
            lowerCharacter();
        }
        else if (isSliding)
        {
            slideVelocity = 0.0f;
            isSliding = false;

            if (Physics.CheckSphere(spaceCheck.position, 0.6f, environmentMask))
            {
                isCrouching = true;
            }
        }

        if (Input.GetKey(KeyCode.LeftControl) && !isSliding)
        {
            maxVelocity = crouchVelocity;
            isCrouching = true;
            lowerCharacter();
        }
        else if (Physics.CheckSphere(spaceCheck.position, 0.6f, environmentMask) && isCrouching)
        {
            maxVelocity = crouchVelocity;
            isCrouching = true;
            lowerCharacter();
        }
        else
        {
            isCrouching = false;
        }

        if (Input.GetKey(KeyCode.LeftShift) && !isCrouching && characterController.isGrounded)
        {
            maxVelocity = runningVelocity;
        }

        // handling friction / drag
        if (!characterController.isGrounded)
        {
            smoothDirectionSpeed = 0.4f;
        }
        else
        {
            smoothDirectionSpeed = 0.05f;
        }

        currentCrouchValue = Mathf.SmoothDamp(currentCrouchValue, crouchValue, ref smoothCrouchValue, 0.1f);
        playerCameraContainer.transform.localPosition = new Vector3(0, currentCrouchValue, 0);
    }

    public void lowerCharacter()
    {
        characterController.height = 1;
        characterController.center = new Vector3(0, -0.5f, 0);
        crouchValue = -0.2f;
    }
    public void upperCharacter()
    {
        characterController.height = 2;
        characterController.center = new Vector3(0, 0, 0);
        crouchValue = 0.5f;
    }
}