Third-person Character Controller (Unity 6 / C#)
A small, runnable third-person player for a first 3D scene: camera-relative movement, turning, gravity and jump on Unity’s built-in CharacterController, read through the Input System. It is the “write it yourself” alternative to Unity’s Starter Assets, and it pairs with a Cinemachine follow camera.
Theory: unity-character-controller. Scene to walk around in: 3d-blocking-and-greyboxing and unity-probuilder.
Files
| Script | Responsibility | Key wiki concept |
|---|---|---|
SimplePlayerController.cs | Reads Move/Jump, moves relative to the camera, turns to face movement, applies gravity, jump and coyote time, one Move call per frame | unity-character-controller, unity-input-system |
ControllerRigidbodyPusher.cs (optional) | Uses OnControllerColliderHit to shove non-kinematic Rigidbodies sideways | unity-character-controller |
Both scripts are in the GDnD.ThreeD namespace. Neither uses a Rigidbody on the
player.
Requirements
- Unity 6 (6000.x), any 3D template (the Universal 3D template is assumed).
- The Input System package. New Unity 6 projects already have it and use it as the active input handler. If your project only has the old Input Manager, install Input System from the Package Manager first.
- Optional: Cinemachine (3.x) for the follow camera.
Scene setup (about 10 minutes)
- Build or open a scene with a floor that has a collider (a ProBuilder floor has a Mesh Collider by default; a plain Plane or Cube has its own collider).
- GameObject > 3D Object > Capsule, name it
Player, and set its position so it stands on the floor (a default capsule is 2 m tall, so Y = 1 on a floor at 0). - Remove the Capsule Collider and Add Component > Character Controller. Leave Height 2 and Radius 0.5; set Skin Width to about 0.08 (at least 10% of the radius) and Step Offset to about 0.3. Do not add a Rigidbody.
- Add
SimplePlayerController. Leave the three reference fields empty to useCamera.mainand the project-wide Move and Jump actions, or drag in your own camera andInputActionReferences. - If the Console says there is no “Move” action, open Edit > Project Settings > Input System Package and create the project-wide actions asset (it contains default Move and Jump actions).
- Press Play and move with the default bindings (keyboard or gamepad). To see or change which keys and buttons drive Move and Jump, open the actions asset in the Actions Editor.
Optional: Cinemachine follow camera
- Create an empty child of
PlayernamedCameraTargetat about 1.5 m above its feet. - Right-click
Player> Cinemachine > Targeted Cameras > Follow Camera. Set Tracking Target toCameraTarget. Unity adds a Cinemachine Brain to the Main Camera. - On Cinemachine Follow, set Binding Mode to World Space or Lazy Follow and a Follow Offset of roughly (0, 4, -7). With a “Lock To Target” mode the camera turns whenever the player turns, which feeds back into camera-relative movement and makes the player circle.
- The script reads the Main Camera’s transform, which Cinemachine drives, so no extra wiring is needed.
Optional: pushable crates
Add ControllerRigidbodyPusher to Player. Put a few cubes with a
Rigidbody (mass 1 to 5) in the room and walk into them.
What to try
- Feel numbers. Change
moveSpeed,turnSpeed,gravityandjumpHeightone at a time. Which change makes the jump feel least “floaty”? - Break it on purpose. Set
groundedStickSpeedto 0 and walk down a ramp while watching jump reliability. Then setcoyoteTimeto 0 and try jumping at the very edge of a platform. - Steps and slopes. Make ProBuilder stairs with 0.2 m and then 0.5 m risers. Which ones can the player climb with Step Offset 0.3? Then build a 50° ramp and change Slope Limit.
- Animator hook. Read
HorizontalSpeedfrom another script and feed it into an Animator float parameter. - Compare. Rebuild the same movement with a Rigidbody (moving in
FixedUpdate) and note what you had to handle differently: friction on walls, step-climbing, being knocked back by other bodies.
Notes
- Compile-checked against the Unity 6000.3.11f1 engine assemblies and the Input System assembly that ships with that editor’s 3D template (no stubs). It has not been run in a scene by the author of this README; test it before relying on it in class.
- Movement happens in
Update, notFixedUpdate: a CharacterController is not simulated by the physics engine, and the script scales everything byTime.deltaTime.