DEV Community

Cover image for Unity Character Controller Tutorial: Create Smooth Player Movement with C#
Alok Krishali
Alok Krishali

Posted on

Unity Character Controller Tutorial: Create Smooth Player Movement with C#


Your character can move. Great! But what happens when it reaches a wall? What about gravity, slopes, or jumping?

If you're building a 3D Unity game, simply changing the player's Transform isn't always enough. For a more reliable character controller, Unity provides the CharacterController component.

In this beginner-friendly tutorial, we'll use CharacterController to create a simple player controller with movement, collision detection, gravity, and jumping.

What Is CharacterController in Unity?

CharacterController is a Unity component designed for controlling characters without using a Rigidbody for their basic movement.

It can help your player:

  • Move around the environment
  • Collide with walls and other objects
  • Stay on the ground
  • Handle slopes and steps
  • Move without passing through colliders

It's commonly used for first-person and third-person player controllers.

Note: CharacterController is not a physics Rigidbody. If you need your player to behave like a physics object, a Rigidbody may be a better choice.

Step 1: Create a Player

For testing, let's create a simple Capsule.

In Unity:

Hierarchy → Right-click → 3D Object → Capsule

Rename it:

Player
Enter fullscreen mode Exit fullscreen mode

Set its position to:

X = 0
Y = 1
Z = 0
Enter fullscreen mode Exit fullscreen mode

You can later replace the Capsule with your actual character model.

Step 2: Add CharacterController

Select your Player object.

In the Inspector, select:

Add Component → Character Controller

Unity will add a CharacterController component.

You should see properties such as:

  • Center
  • Radius
  • Height
  • Slope Limit
  • Step Offset

For a Capsule player, Unity's default values are usually a good starting point.

Step 3: Create the Player Movement Script

Create a new C# script:

PlayerController
Enter fullscreen mode Exit fullscreen mode

Open it and add:

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float gravity = -9.81f;
    public float jumpHeight = 1.5f;

    private CharacterController controller;
    private Vector3 velocity;

    void Start()
    {
        controller = GetComponent<CharacterController>();
    }

    void Update()
    {
        float horizontal = Input.GetAxisRaw("Horizontal");
        float vertical = Input.GetAxisRaw("Vertical");

        Vector3 direction = new Vector3(horizontal, 0f, vertical);
        direction = direction.normalized;

        controller.Move(direction * moveSpeed * Time.deltaTime);

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

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

        velocity.y += gravity * Time.deltaTime;

        controller.Move(velocity * Time.deltaTime);
    }
}
Enter fullscreen mode Exit fullscreen mode

Attach the script to your Player GameObject.

Step 4: Test Player Movement

Press Play.

You can now use:

  • W / Up Arrow → Move forward
  • S / Down Arrow → Move backward
  • A / Left Arrow → Move left
  • D / Right Arrow → Move right
  • Space → Jump

The CharacterController will handle collisions with objects that have colliders.

How Does CharacterController.Move() Work?

The main movement happens here:

controller.Move(direction * moveSpeed * Time.deltaTime);
Enter fullscreen mode Exit fullscreen mode

Instead of directly changing the player's transform, we're asking the CharacterController to move the character.

This is important because the controller can detect collisions while moving.

For example, if your player walks toward a wall with a Collider, the CharacterController won't simply move through it.

Why Use .normalized?

We create our movement direction with:

Vector3 direction = new Vector3(horizontal, 0f, vertical);
direction = direction.normalized;
Enter fullscreen mode Exit fullscreen mode

Without normalization, moving diagonally could make the player move faster than moving in one direction.

.normalized keeps the direction's length consistent.

Adding Gravity

One common beginner mistake is assuming that CharacterController automatically applies gravity.

It doesn't.

That's why we create:

private Vector3 velocity;
Enter fullscreen mode Exit fullscreen mode

Then apply gravity:

velocity.y += gravity * Time.deltaTime;
Enter fullscreen mode Exit fullscreen mode

Finally, we move the controller vertically:

controller.Move(velocity * Time.deltaTime);
Enter fullscreen mode Exit fullscreen mode

This gives the character natural downward movement.

Keeping the Player on the Ground

We use:

if (controller.isGrounded && velocity.y < 0)
{
    velocity.y = -2f;
}
Enter fullscreen mode Exit fullscreen mode

controller.isGrounded tells us whether the character is touching the ground.

Setting a small negative value instead of 0 helps keep the controller properly grounded.

Adding Jumping

The jump happens here:

if (Input.GetButtonDown("Jump") && controller.isGrounded)
{
    velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
Enter fullscreen mode Exit fullscreen mode

The important part is the controller.isGrounded check.

It prevents the player from continuously jumping while already in the air.

You can make the jump higher by increasing:

jumpHeight = 1.5f;
Enter fullscreen mode Exit fullscreen mode

For example:

1.0 → Small jump
1.5 → Normal jump
3.0 → High jump
Enter fullscreen mode Exit fullscreen mode

Make Sure Your Floor Has a Collider

Your CharacterController needs something to collide with.

For example, create a Plane:

Hierarchy → 3D Object → Plane

A Unity Plane already has a MeshCollider.

You can also use cubes as floors and walls. A Cube has a BoxCollider by default.

Your scene might look like:

Scene
├── Main Camera
├── Directional Light
├── Player
│   ├── CharacterController
│   └── PlayerController
├── Ground
│   └── Collider
└── Wall
    └── Collider
Enter fullscreen mode Exit fullscreen mode

CharacterController vs Rigidbody

Both approaches can be useful, but they solve slightly different problems.

CharacterController Rigidbody
Designed for character movement Physics-based
Easy to control directly Uses physics simulation
Great for FPS/TPS characters Great for physics-driven characters
Handles character collisions Handles forces and physics
Gravity must be coded Gravity can come from physics

For a traditional player-controlled character, CharacterController is a good starting point.

Common Beginner Problems

Player Falls Through the Floor

Make sure the floor has a Collider.

Player Doesn't Move

Check that:

  1. PlayerController is attached.
  2. CharacterController is attached.
  3. You're pressing the correct movement keys.
  4. The GameObject is active.

Player Can Jump in the Air

Make sure your jump code includes:

controller.isGrounded
Enter fullscreen mode Exit fullscreen mode

Player Moves Too Fast

Lower:

moveSpeed = 5f;
Enter fullscreen mode Exit fullscreen mode

Try 3f or 4f.

What Can You Add Next?

Once this basic controller works, you can expand it with:

  • Sprinting
  • Crouching
  • Smooth character rotation
  • First-person camera
  • Third-person camera
  • Animation transitions
  • Footstep sounds
  • Stamina
  • Mobile joystick controls
  • Slope handling

The important thing is to build these features one at a time.
Check here - Unity game development tutorials

Final Thoughts

A CharacterController gives you a solid starting point for building a 3D player in Unity. Instead of simply moving the Transform, you get a controller designed specifically for character movement and collision handling.

Start with basic movement, then add gravity and jumping. Once you understand how CharacterController.Move(), isGrounded, and velocity work together, you'll have a much better foundation for creating more advanced player controllers.

Did you use CharacterController or Rigidbody in your first Unity game? Share your experience in the comments!

Top comments (0)