Third-person orbit
Assets/
Scenes/
Arena.unity
Scripts/
you already havethis lesson addsthis lesson retires
Step two. FollowCamera watches from a fixed angle — you can never look behind the Knight. Remove it from Main Camera and put this on instead.
A third-person camera is a camera on the end of a stick. The stick is anchored on the character; the mouse rotates it.
Mouse X spins the stick horizontally (yaw), mouse Y raises and lowers it (pitch).
The orbit rig
using UnityEngine;
public class OrbitCamera : MonoBehaviour
{
[SerializeField] private Transform target; // the Knight's CameraTarget child, from tab 02
[SerializeField] private float distance = 6f;
[SerializeField] private float sensitivity = 240f;
[SerializeField] private float minPitch = -25f;
[SerializeField] private float maxPitch = 65f;
[SerializeField] private LayerMask obstacles;
private float yaw;
private float pitch = 20f;
void Start()
{
Cursor.lockState = CursorLockMode.Locked; // hide and centre the cursor
Cursor.visible = false;
}
void LateUpdate()
{
// ---- 1. accumulate the angles
yaw += Input.GetAxis("Mouse X") * sensitivity * Time.deltaTime;
pitch -= Input.GetAxis("Mouse Y") * sensitivity * Time.deltaTime;
pitch = Mathf.Clamp(pitch, minPitch, maxPitch); // never flip over
// ---- 2. build the rotation, then push back along -Z
Quaternion rot = Quaternion.Euler(pitch, yaw, 0f);
Vector3 pivot = target.position;
Vector3 wanted = pivot + rot * new Vector3(0f, 0f, -distance);
// ---- 3. pull in if a wall is in the way
float realDistance = distance;
if (Physics.SphereCast(pivot, 0.3f, (wanted - pivot).normalized,
out RaycastHit hit, distance, obstacles))
realDistance = hit.distance - 0.2f;
transform.position = pivot + rot * new Vector3(0f, 0f, -realDistance);
transform.rotation = rot;
}
public float Yaw => yaw; // movement uses this to know 'forward'
}pitch -= mouseY because in Unity a positive pitch tilts the camera down. Subtracting gives the standard 'push forward = look up' feel.
Clamping pitch is not optional. Without it the camera passes over the head and the world turns upside down.
rot * new Vector3(0, 0, -distance) is the whole trick: rotate a backwards vector, and you get the camera position.
Wire it into movement
Your PlayerMovement already reads Camera.main.transform.eulerAngles.y, so this camera works with it immediately — moving forward now means away from this camera.
Mouse sensitivity that does not lie
- 1
Mouse X/Y are already per-frame deltas, so multiplying by Time.deltaTime is technically wrong — but it makes the Inspector number stable across frame rates. Pick one convention and keep it.
- 2
Expose sensitivity in a settings menu. Every player has a different mouse DPI.
- 3
Add an Invert Y toggle. Roughly one player in five needs it, and they will bounce off your game without it.
if (Input.GetKeyDown(KeyCode.Escape))
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}Common mistakes
Storing yaw and pitch in transform.eulerAngles and reading them back breaks the clamp: Unity wraps the angles to 0–360, so pitch reads 350 instead of -10. Keep the two angles in your own float fields.
A second camera tagged MainCamera, added for a menu or by Cinemachine, makes Camera.main return whichever it finds first, and forward flips. Keep one MainCamera tag, or read Yaw from this script instead.
obstacles left at Nothing means the SphereCast never hits and the camera still clips through walls; set it to the level geometry layer. Never include the Player layer, or the camera hides behind the character itself.