Follow camera
Assets/
Scenes/
Arena.unity
Scripts/
you already havethis lesson addsthis lesson retires
Open Arena.unity. SwapCamera from tab 01 already follows whoever is active, but it snaps, it stares at the feet, and it happily slides through walls. This tab rebuilds it in three steps, and this is step one.
A camera glued to the player feels cheap. A camera that chases the player with a slight lag feels like a game.
The player moves first, the camera catches up over the next few frames.
The follow script
using UnityEngine;
public class FollowCamera : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private Vector3 offset = new Vector3(0f, 6f, -8f);
[SerializeField] private float smoothTime = 0.15f;
[SerializeField] private Vector3 lookAtOffset = new Vector3(0f, 1.5f, 0f);
private Vector3 velocity; // SmoothDamp keeps its own state here
void LateUpdate()
{
if (target == null) return;
Vector3 wanted = target.position + offset;
transform.position = Vector3.SmoothDamp(transform.position, wanted, ref velocity, smoothTime);
transform.LookAt(target.position + lookAtOffset);
}
}Lerp or SmoothDamp?
✓ Vector3.Lerp(a, b, t * dt)
✓ Vector3.SmoothDamp(...)
Keep following the active character
Hard-coding target to the Knight would break pressing 2 and 3. Ask the switcher instead — exactly as SwapCamera did.
[SerializeField] private CharacterSwitcher switcher;
void LateUpdate()
{
Transform target = switcher.ActiveCharacter; // not a fixed reference
Vector3 wanted = target.position + offset;
transform.position = Vector3.SmoothDamp(transform.position, wanted, ref velocity, smoothTime);
transform.LookAt(target.position + lookAtOffset);
}Why LateUpdate, always
Unity runs every Update in an undefined order. Your camera might run before the player moves.
LateUpdate runs after all Updates are done, so the camera always sees the final position of that frame.
Symptom of getting this wrong: the character visibly vibrates by one or two pixels while moving.
Look-ahead — see where you are going
Shifting the camera slightly in the direction of travel gives the player more warning about what is coming. It costs three lines.
[SerializeField] private float lookAhead = 2.5f;
private CharacterController targetCC;
void LateUpdate()
{
Vector3 ahead = targetCC.velocity.normalized * lookAhead;
ahead.y = 0f;
Vector3 wanted = target.position + offset + ahead;
transform.position = Vector3.SmoothDamp(transform.position, wanted, ref velocity, smoothTime);
transform.LookAt(target.position + lookAtOffset);
}Stop the camera clipping through walls
[SerializeField] private LayerMask obstacles;
Vector3 pivot = target.position + lookAtOffset;
Vector3 dir = (wanted - pivot).normalized;
float dist = Vector3.Distance(pivot, wanted);
// is there a wall between the character and where the camera wants to be?
if (Physics.SphereCast(pivot, 0.3f, dir, out RaycastHit hit, dist, obstacles))
wanted = hit.point - dir * 0.25f; // pull the camera in front of itCommon mistakes
smoothTime of 0 snaps like the old SwapCamera did; above 0.5 s the camera lags so far behind that the player runs off screen. Keep it between 0.1 and 0.25 s and tune from there.
Declaring velocity inside LateUpdate resets SmoothDamp every frame, and the motion turns springy and wrong. It must stay a field, because SmoothDamp keeps its state there between frames.
Main Camera dragged under the character in the Hierarchy inherits every rotation of its parent and fights this script. Keep the camera at the root of the scene and let the script do the following.