Want more features? Check out the with advanced orbit camera with lock-on targeting, shoulder swap, cinematic mode, and swappable camera profiles for versatile third person gameplay.
intermediate Movement

Third Person Camera

Orbit camera with collision detection, zoom, and vertical angle limits. Ideal for action-adventure and RPG games.

Unity 2022.3+ · 3.6 KB · ThirdPersonCamera.cs

How to Use

1

Create an empty CameraRig object and parent your Camera to it

2

Attach ThirdPersonCamera to the CameraRig

3

Assign your player Transform as the Target

4

Adjust distance, zoom range, and rotation speed

5

Set collision layers to avoid clipping through walls

6

Camera auto-avoids obstacles via SphereCast

Features

  • Orbit camera with mouse-controlled horizontal and vertical rotation
  • SphereCast collision avoidance to prevent clipping through walls
  • Scroll wheel zoom with smooth interpolation and min/max limits
  • Configurable vertical angle clamping for orbit range
  • Target offset for shoulder-level or head-level camera positioning
  • SetTarget method for runtime target switching (e.g., cutscenes)

When to Use This

Ideal for third-person action-adventure games, RPGs, hack-and-slash, and open-world exploration titles. Use this when you need an orbiting camera that the player controls with the mouse while automatically avoiding geometry. Works well with the 3D Player Controller for a complete third-person character setup.

Common Mistakes

The Camera must be a child of the CameraRig object — attach this script to the rig, not the camera itself. If collisionLayers is set to Everything (~0), the camera will collide with the player's own collider, causing jitter — exclude the player's layer. Setting minDistance too close to zero can cause the camera to clip inside the character model when pushed against walls.

Source Code

ThirdPersonCamera.cs
C#
using UnityEngine;

/// <summary>
/// Third person orbit camera with collision avoidance.
/// Attach to an empty CameraRig object, parent your Camera as a child.
/// </summary>
public class ThirdPersonCamera : MonoBehaviour
{
    [Header("Target")]
    [SerializeField] private Transform target;
    [SerializeField] private Vector3 targetOffset = new Vector3(0f, 1.5f, 0f);

    [Header("Orbit")]
    [SerializeField] private float rotationSpeed = 5f;
    [SerializeField] private float minVerticalAngle = -30f;
    [SerializeField] private float maxVerticalAngle = 70f;

    [Header("Distance")]
    [SerializeField] private float defaultDistance = 5f;
    [SerializeField] private float minDistance = 1.5f;
    [SerializeField] private float maxDistance = 15f;
    [SerializeField] private float zoomSpeed = 3f;
    [SerializeField] private float zoomSmoothing = 8f;

    [Header("Collision")]
    [SerializeField] private float collisionRadius = 0.3f;
    [SerializeField] private LayerMask collisionLayers = ~0;

    [Header("Smoothing")]
    [SerializeField] private float followSmoothing = 10f;

    private float currentX;
    private float currentY = 15f;
    private float currentDistance;
    private float targetDistance;
    private Camera cam;

    private void Awake()
    {
        cam = GetComponentInChildren<Camera>();
        currentDistance = defaultDistance;
        targetDistance = defaultDistance;
    }

    private void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }

    private void LateUpdate()
    {
        if (target == null) return;

        HandleInput();
        UpdatePosition();
    }

    private void HandleInput()
    {
        if (Cursor.lockState != CursorLockMode.Locked) return;

        currentX += Input.GetAxis("Mouse X") * rotationSpeed;
        currentY -= Input.GetAxis("Mouse Y") * rotationSpeed;
        currentY = Mathf.Clamp(currentY, minVerticalAngle, maxVerticalAngle);

        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (Mathf.Abs(scroll) > 0.01f)
        {
            targetDistance -= scroll * zoomSpeed;
            targetDistance = Mathf.Clamp(targetDistance, minDistance, maxDistance);
        }
    }

    private void UpdatePosition()
    {
        currentDistance = Mathf.Lerp(currentDistance, targetDistance, Time.deltaTime * zoomSmoothing);

        Vector3 focusPoint = target.position + targetOffset;
        Quaternion rotation = Quaternion.Euler(currentY, currentX, 0f);
        Vector3 desiredPosition = focusPoint + rotation * new Vector3(0f, 0f, -currentDistance);

        // Collision check
        float adjustedDistance = currentDistance;
        Vector3 direction = desiredPosition - focusPoint;

        if (Physics.SphereCast(focusPoint, collisionRadius, direction.normalized,
            out RaycastHit hit, currentDistance, collisionLayers))
        {
            adjustedDistance = hit.distance - collisionRadius;
            adjustedDistance = Mathf.Max(adjustedDistance, minDistance * 0.5f);
        }

        Vector3 finalPosition = focusPoint + rotation * new Vector3(0f, 0f, -adjustedDistance);
        transform.position = Vector3.Lerp(transform.position, finalPosition, Time.deltaTime * followSmoothing);
        transform.LookAt(focusPoint);
    }

    /// <summary>
    /// Set a new target to follow at runtime.
    /// </summary>
    public void SetTarget(Transform newTarget)
    {
        target = newTarget;
    }
}
Ready for more? Third Person Camera Pro Advanced orbit camera with lock-on targeting, shoulder swap, cinematic mode, and swappable camera profiles for versatile third person gameplay.