using UnityEngine; [AddComponentMenu("Camera/Top Down Follow Camera")] public class TopDownCameraController : MonoBehaviour { [Header("Target")] public Transform target; [Header("Positioning")] public float distance = 8f; // horizontal distance from target public float height = 6f; // vertical offset above target [Range(0f, 89f)] public float pitch = 80f; // X rotation in degrees (80 by default) [Header("Smoothing")] public bool smoothPosition = true; public float positionSmoothTime = 0.12f; public bool smoothRotation = true; public float rotationSmoothTime = 0.08f; Vector3 positionVelocity = Vector3.zero; float rotationInterpolation = 0f; // Cached values for optimization private Quaternion cachedPitchRotation; private Vector3 cachedLocalOffset; private bool cachedPitchNeedsUpdate = true; void Reset() { pitch = 80f; distance = 8f; height = 6f; } void OnValidate() { // Invalidate cache when values change in editor cachedPitchNeedsUpdate = true; } void LateUpdate() { if (target == null) return; // Update cached rotation if pitch changed if (cachedPitchNeedsUpdate) { cachedPitchRotation = Quaternion.Euler(pitch, 0f, 0f); cachedLocalOffset = cachedPitchRotation * new Vector3(0f, height, -distance); cachedPitchNeedsUpdate = false; } Vector3 desiredPosition = target.position + cachedLocalOffset; // Smooth position if (smoothPosition) { transform.position = Vector3.SmoothDamp(transform.position, desiredPosition, ref positionVelocity, positionSmoothTime); } else { transform.position = desiredPosition; positionVelocity = Vector3.zero; } // Desired rotation: look at target but keep the pitch Vector3 lookDirection = (target.position - transform.position).normalized; Quaternion desiredRotation = Quaternion.LookRotation(lookDirection, Vector3.up); // Lock X rotation to the pitch value Vector3 euler = desiredRotation.eulerAngles; euler.x = pitch; desiredRotation = Quaternion.Euler(euler); // Smooth rotation using exponential decay if (smoothRotation) { float slerp = 1f - Mathf.Exp(-rotationSmoothTime * 60f * Time.deltaTime); transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, slerp); } else { transform.rotation = desiredRotation; } } }