using System.Collections.Generic; using UnityEngine; /// /// Sits on the "Radar" overlay object (a disc rendered by the Radar Camera in front of the main camera). /// Spawns "Target" blips - clones of the child "Target Template" - each of which continuously points from /// the reference camera toward a world-space transform (visualising the bearing camera -> target) and then /// destroys itself after a given lifetime. See for the per-blip behaviour. /// /// The whole radar overlay is only shown while it is useful: at least one live target, first-person is active, /// AND the match is in its Seek phase. It is hidden by switching the overlay camera's rendering off (not by /// deactivating the GameObjects), so hidden blips keep counting down their lifetime and self-destruct on schedule. /// [DisallowMultipleComponent] public sealed class Radar : MonoBehaviour { /// The scene's single radar overlay (lives in the persistent Main scene). Hunter clients spawn /// hider blips onto it from MatchManager's radar-ping sweep. public static Radar Instance { get; private set; } [Tooltip("Template each spawned Target is cloned from. Auto-resolves to the child \"Target Template\".")] [SerializeField] private Transform targetTemplate; [Tooltip("Camera the direction is measured FROM (camera -> target). Auto-resolves to the Camera this radar sits under.")] [SerializeField] private Camera referenceCamera; [Tooltip("Overlay camera that renders the radar. Its rendering is toggled off unless the radar should show " + "(at least one live target AND first-person active). Auto-resolves to the Camera this radar sits under.")] [SerializeField] private Camera radarCamera; [Tooltip("First-person rig. The radar is only shown while first-person is active. Empty = found automatically.")] [SerializeField] private FirstPersonController firstPerson; // Live blips, pruned as they self-destruct; the radar shows only while at least one is alive. private readonly List _blips = new List(); private void Awake() { Instance = this; if (!targetTemplate) { var t = transform.Find("Target Template"); if (t) targetTemplate = t; } if (!referenceCamera) referenceCamera = GetComponentInParent(); if (!radarCamera) radarCamera = GetComponentInParent(); if (!firstPerson) firstPerson = FindFirstObjectByType(FindObjectsInactive.Include); ApplyVisibility(false); // hidden until a target exists, first-person is live AND the match is seeking } private void OnDestroy() { if (Instance == this) Instance = null; } /// /// Spawns a Target blip that each frame orients toward (from the reference /// camera) and destroys itself after seconds. Returns the spawned blip, or /// null if it could not be created. /// public RadarTarget Spawn(Transform target, float lifetime) { if (!target) { Debug.LogWarning("[Radar] Spawn called without a target transform.", this); return null; } if (!targetTemplate) { Debug.LogWarning("[Radar] No Target Template assigned or found.", this); return null; } Transform clone = Instantiate(targetTemplate, targetTemplate.parent); clone.name = "Target"; clone.localPosition = targetTemplate.localPosition; clone.localScale = targetTemplate.localScale; clone.gameObject.SetActive(true); var blip = clone.GetComponent(); blip.Init(target, referenceCamera, lifetime); _blips.Add(blip); return blip; } // LateUpdate so the count reflects any blip that self-destructed this frame, in step with RadarTarget's aim. private void LateUpdate() { // Drop self-destructed blips (Destroy leaves a fake-null entry behind). for (int i = _blips.Count - 1; i >= 0; i--) if (!_blips[i]) _blips.RemoveAt(i); bool visible = _blips.Count > 0 && firstPerson && firstPerson.enabled && InSeekPhase(); ApplyVisibility(visible); } // The radar only makes sense during the seek phase (hunters chasing hiders). Blips only spawn then anyway, // but a blip's lifetime can outlast the phase edge, so gating here hides the overlay the instant Seek ends. private static bool InSeekPhase() { var m = MatchManager.Instance; return m && m.MatchActive && m.LocalSubPhase == MatchManager.MatchSubPhase.Seek; } // Shows/hides the whole radar overlay in one toggle (disc + blips) by switching the overlay camera's // rendering. The GameObjects stay active, so blips keep ticking down their lifetime while hidden. The // overlay renders only the Radar layer, so nothing else is affected. private void ApplyVisibility(bool visible) { if (radarCamera && radarCamera.enabled != visible) radarCamera.enabled = visible; } }