using NightclubArcadia.Player; using NightclubArcadia.Player.Math; using UnityEngine; namespace NightclubArcadia.Cinematics { /// /// Converts mouse drag-pan into CameraRigAnchor manual offset with recenter rules. /// public sealed class CameraPanInput : MonoBehaviour { [SerializeField] Camera brainCamera; [SerializeField] CameraRigAnchor anchor; [SerializeField] CameraStateDirector stateDirector; [SerializeField] PlayerPointerInput pointerInput; [SerializeField] float panMetresPerPixel = 0.02f; [SerializeField] float maxPanDistance = 12f; [SerializeField] float recenterDelay = 2.5f; [SerializeField] float recenterSmoothTime = 0.8f; [SerializeField] float movementRecenterDelay = 0.4f; float releaseTimer = float.MaxValue; Vector3 recenterVelocity; Vector3 camRight; Vector3 camForward; bool basisInitialized; public bool IsManualPanActive => pointerInput != null && pointerInput.IsPanning || releaseTimer < recenterDelay; void Awake() { if (anchor == null) { anchor = GetComponent(); } if (stateDirector == null) { stateDirector = GetComponent(); } if (brainCamera == null) { brainCamera = Camera.main; } } void Start() { if (pointerInput == null) { var player = GameObject.FindGameObjectWithTag("Player"); if (player != null) { pointerInput = player.GetComponent(); } } if (pointerInput != null) { pointerInput.PanDelta += OnPanDelta; pointerInput.PanEnded += OnPanEnded; } } void OnDestroy() { if (pointerInput != null) { pointerInput.PanDelta -= OnPanDelta; pointerInput.PanEnded -= OnPanEnded; } } void Update() { if (anchor == null || stateDirector == null) { return; } if (stateDirector.AllowsManualPan && pointerInput != null && pointerInput.IsPanning) { releaseTimer = 0f; } else if (releaseTimer < recenterDelay) { releaseTimer += Time.deltaTime; } if (ShouldShortenRecenter()) { releaseTimer = Mathf.Max(releaseTimer, recenterDelay - movementRecenterDelay); } if (!stateDirector.AllowsManualPan) { return; } if (releaseTimer >= recenterDelay && (pointerInput == null || !pointerInput.IsPanning)) { anchor.ManualPanOffset = Vector3.SmoothDamp( anchor.ManualPanOffset, Vector3.zero, ref recenterVelocity, recenterSmoothTime); } anchor.ClampManualPan(maxPanDistance); } void OnPanDelta(Vector2 delta) { if (stateDirector == null || !stateDirector.AllowsManualPan || anchor == null) { return; } EnsureCameraBasis(); anchor.Nudge(MotionInputMath.ScreenDeltaToWorldPan(delta, camRight, camForward, panMetresPerPixel)); anchor.ClampManualPan(maxPanDistance); } void OnPanEnded() { releaseTimer = 0f; } bool ShouldShortenRecenter() { var player = GameObject.FindGameObjectWithTag("Player"); if (player == null) { return false; } var arbiter = player.GetComponent(); return arbiter != null && arbiter.ActiveSource != MotionSource.None; } void EnsureCameraBasis() { if (brainCamera == null) { brainCamera = Camera.main; } if (brainCamera == null) { return; } if (!basisInitialized) { camRight = brainCamera.transform.right; camForward = brainCamera.transform.forward; basisInitialized = true; } } } }