Want more features? Check out the with advanced 3d character controller with crouch, slide, ladder climbing, swimming, stamina system, and footstep audio.
Part of these game systems:
intermediate Movement

3D Player Controller

First/third person character controller with smooth movement, jumping, and slope handling using CharacterController.

Unity 2022.3+ · 4.1 KB · PlayerController3D.cs

How to Use

1

Attach to your player GameObject

2

Add a CharacterController component

3

Create an empty child at the feet for Ground Check

4

Set Ground Mask to your terrain layer

5

Sprint with Left Shift

Features

  • Camera-relative movement so the player moves where the camera faces
  • Sprint toggle with Left Shift and separate walk/sprint speeds
  • Smooth rotation via LerpAngle for natural turning
  • Configurable gravity and jump height using physics formula
  • Ground detection via Physics.CheckSphere with Gizmo visualization
  • Automatic CharacterController requirement via RequireComponent

When to Use This

Ideal for third-person action games, RPGs, adventure games, and horror games that need solid ground movement with sprinting. Use this when you want CharacterController-based movement (no Rigidbody physics) with camera-relative input. Works great as the foundation for any 3D game with a controllable character.

Common Mistakes

Not assigning the cameraTransform field and having no Camera tagged MainCamera will cause a NullReferenceException on startup. The groundCheck Transform must be positioned at the character's feet, not the center — otherwise ground detection fails. Setting gravity to a positive value will launch the character upward instead of pulling them down.

Source Code

PlayerController3D.cs
C#
using UnityEngine;

[RequireComponent(typeof(CharacterController))]
public class PlayerController3D : MonoBehaviour
{
    [Header("Movement")]
    [SerializeField] private float walkSpeed = 6f;
    [SerializeField] private float sprintSpeed = 10f;
    [SerializeField] private float rotationSpeed = 10f;

    [Header("Jumping")]
    [SerializeField] private float jumpHeight = 1.5f;
    [SerializeField] private float gravity = -20f;

    [Header("Ground Check")]
    [SerializeField] private Transform groundCheck;
    [SerializeField] private float groundDistance = 0.3f;
    [SerializeField] private LayerMask groundMask;

    [Header("Camera")]
    [SerializeField] private Transform cameraTransform;

    private CharacterController controller;
    private Vector3 velocity;
    private bool isGrounded;

    private void Awake()
    {
        controller = GetComponent<CharacterController>();
        if (cameraTransform == null)
            cameraTransform = Camera.main.transform;
    }

    private void Update()
    {
        // Ground check
        isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);

        if (isGrounded && velocity.y < 0f)
            velocity.y = -2f;

        // Input
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        Vector3 inputDir = new Vector3(h, 0f, v).normalized;

        // Movement relative to camera
        if (inputDir.magnitude >= 0.1f)
        {
            float targetAngle = Mathf.Atan2(inputDir.x, inputDir.z) * Mathf.Rad2Deg + cameraTransform.eulerAngles.y;
            float angle = Mathf.LerpAngle(transform.eulerAngles.y, targetAngle, rotationSpeed * Time.deltaTime);
            transform.rotation = Quaternion.Euler(0f, angle, 0f);

            Vector3 moveDir = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;
            float speed = Input.GetKey(KeyCode.LeftShift) ? sprintSpeed : walkSpeed;
            controller.Move(moveDir.normalized * speed * Time.deltaTime);
        }

        // Jump
        if (Input.GetButtonDown("Jump") && isGrounded)
            velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);

        // Apply gravity
        velocity.y += gravity * Time.deltaTime;
        controller.Move(velocity * Time.deltaTime);
    }

    private void OnDrawGizmosSelected()
    {
        if (groundCheck != null)
        {
            Gizmos.color = isGrounded ? Color.green : Color.red;
            Gizmos.DrawWireSphere(groundCheck.position, groundDistance);
        }
    }
}
Ready for more? 3D Player Controller Pro Advanced 3D character controller with crouch, slide, ladder climbing, swimming, stamina system, and footstep audio.