using System.Collections.Generic; using UnityEngine; namespace NightclubArcadia.Cinematics { /// /// Scores registered POIs and writes bias onto the camera anchor. /// public sealed class CameraPointOfInterestDirector : MonoBehaviour { static readonly List Registered = new List(); [SerializeField] CameraRigAnchor anchor; [SerializeField] CameraStateDirector stateDirector; [SerializeField] float poiSmoothTime = 1f; [SerializeField] float poiReleaseTime = 1.4f; [SerializeField] float maxHeadingAngle = 100f; Vector3 poiVelocity; float distanceVelocity; CameraPointOfInterest activePoi; public bool HasActivePoi => activePoi != null; public static void Register(CameraPointOfInterest poi) { if (poi != null && !Registered.Contains(poi)) { Registered.Add(poi); } } public static void Unregister(CameraPointOfInterest poi) { Registered.Remove(poi); } void Awake() { if (anchor == null) { anchor = GetComponent(); } if (stateDirector == null) { stateDirector = GetComponent(); } } void LateUpdate() { if (anchor == null || stateDirector == null) { return; } var follow = anchor.FollowTarget; if (follow == null) { return; } if (!stateDirector.AllowsPointOfInterest) { return; } if (stateDirector.Current == CameraState.ManualPan) { return; } activePoi = FindBestPoi(follow); if (activePoi == null) { ReleasePoi(); return; } var toPoi = activePoi.Target.position - follow.position; toPoi.y = 0f; var targetBias = Vector3.ClampMagnitude(toPoi, activePoi.MaxBias); anchor.PoiBias = Vector3.SmoothDamp( anchor.PoiBias, targetBias, ref poiVelocity, poiSmoothTime); anchor.DistanceBias = Mathf.SmoothDamp( anchor.DistanceBias, activePoi.ExtraDistance, ref distanceVelocity, poiSmoothTime); } CameraPointOfInterest FindBestPoi(Transform follow) { CameraPointOfInterest best = null; var bestScore = 0f; var forward = follow.forward; foreach (var poi in Registered) { if (poi == null || !poi.isActiveAndEnabled) { continue; } var offset = poi.Target.position - follow.position; offset.y = 0f; var distance = offset.magnitude; if (distance > poi.TriggerRadius || distance <= 0.01f) { continue; } var angle = Vector3.Angle(forward, offset); if (angle > maxHeadingAngle) { continue; } var score = poi.Weight * (1f - distance / poi.TriggerRadius); if (score > bestScore) { bestScore = score; best = poi; } } return best; } void ReleasePoi() { if (anchor == null) { return; } anchor.PoiBias = Vector3.SmoothDamp( anchor.PoiBias, Vector3.zero, ref poiVelocity, poiReleaseTime); anchor.DistanceBias = Mathf.SmoothDamp( anchor.DistanceBias, 0f, ref distanceVelocity, poiReleaseTime); if (anchor.PoiBias.sqrMagnitude < 0.0001f && Mathf.Abs(anchor.DistanceBias) < 0.01f) { activePoi = null; } } } }