using UnityEngine; using DG.Tweening; public class CrateEscapeCameraFollow : MonoBehaviour { [Header("Targets")] public Transform player; // follow target public Transform topDownAnchor; // start view (pos+rot) public Transform gameplayAnchor; // after-intro view (pos+rot) [Header("Timing")] public float introHoldSeconds = 3f; // top-down hold public float transitionSeconds = 1f; // tween duration to gameplay view public bool useUnscaledTime = false; // set true if you pause timeScale during intro [Header("Follow")] public float followSmoothTime = 0.20f; // SmoothDamp time public float maxFollowSpeed = Mathf.Infinity; // runtime private Vector3 _worldOffset; private Quaternion _lockedRotation; private Vector3 _vel; private bool _following; private Sequence _seq; void Start() { if (!player || !gameplayAnchor) { Debug.LogError("[CameraFollow] Assign player & gameplayAnchor."); enabled = false; return; } // snap to top-down if provided, otherwise keep current if (topDownAnchor) { transform.position = topDownAnchor.position; transform.rotation = topDownAnchor.rotation; } StartCoroutine(BeginFlow()); } System.Collections.IEnumerator BeginFlow() { // 1) Hold top-down if (introHoldSeconds > 0f) { if (useUnscaledTime) yield return new WaitForSecondsRealtime(introHoldSeconds); else yield return new WaitForSeconds(introHoldSeconds); } // 2) Tween to your provided gameplay pose _seq?.Kill(); _seq = DOTween.Sequence().SetUpdate(useUnscaledTime) .Join(transform.DOMove(gameplayAnchor.position, transitionSeconds).SetEase(Ease.InOutSine)) .Join(transform.DORotateQuaternion(gameplayAnchor.rotation, transitionSeconds).SetEase(Ease.InOutSine)) .OnComplete(() => { // 3) Lock rotation & compute world offset for follow _lockedRotation = gameplayAnchor.rotation; _worldOffset = gameplayAnchor.position - player.position; _following = true; }); yield break; } void LateUpdate() { if (!_following || !player) return; // keep rotation fixed (no camera roll/yaw changes during follow) if (transform.rotation != _lockedRotation) transform.rotation = _lockedRotation; Vector3 targetPos = player.position + _worldOffset; if (useUnscaledTime) { transform.position = Vector3.SmoothDamp( transform.position, targetPos, ref _vel, followSmoothTime, maxFollowSpeed, Time.unscaledDeltaTime); } else { transform.position = Vector3.SmoothDamp( transform.position, targetPos, ref _vel, followSmoothTime, maxFollowSpeed); } } void OnDisable() => _seq?.Kill(); /// Call if the player respawns or you reposition the gameplayAnchor at runtime. public void RecenterToGameplayAnchor() { _lockedRotation = gameplayAnchor.rotation; _worldOffset = gameplayAnchor.position - player.position; } }