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; Quaternion rotationVelocity; void Reset() { pitch = 80f; distance = 8f; height = 6f; } void LateUpdate() { if (target == null) return; // Calculate desired camera position in local space relative to target // Start with a rotation that looks down by 'pitch' degrees Quaternion pitchRot = Quaternion.Euler(pitch, 0f, 0f); // Desired offset: move back along the camera's forward by 'distance', then up by 'height' Vector3 localOffset = pitchRot * new Vector3(0f, height, -distance); Vector3 desiredPosition = target.position + localOffset; // Smooth position if (smoothPosition) { transform.position = Vector3.SmoothDamp(transform.position, desiredPosition, ref positionVelocity, positionSmoothTime); } else { transform.position = desiredPosition; } // Desired rotation: look at target but keep the pitch Vector3 lookDirection = (target.position - transform.position).normalized; Quaternion desiredRotation = Quaternion.LookRotation(lookDirection, Vector3.up); // Optionally lock X rotation to the pitch value to avoid subtle tilts Vector3 euler = desiredRotation.eulerAngles; euler.x = pitch; desiredRotation = Quaternion.Euler(euler); // Smooth rotation if (smoothRotation) { transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, 1f - Mathf.Exp(-rotationSmoothTime * 60f * Time.deltaTime)); } else { transform.rotation = desiredRotation; } } }