using System.Collections; using System.Collections.Generic; using UnityEngine; using static LaserBeam; public class LaserBeamController : MonoBehaviour { [Header("Cycle Durations")] public float interval = 20f; // how long each batch runs before switching public float idleBeforeCharge = 3f; // Idle time before Charging public float chargingWindup = 1f; // Charging duration (blink) public float fireDuration = 1f; // Firing duration (lethal) [Header("Beams (auto-populated)")] public List allLasers = new List(); private readonly List> batches = new(); private readonly List activeLasers = new(); void Start() { // Use active-only; if you need inactive too, use FindObjectsOfType(true) allLasers.AddRange(FindObjectsOfType()); foreach (var lb in allLasers) { if (!lb) continue; lb.externalControl = true; // controller owns timing lb.ResetCycle(); // Idle + line off lb.enabled = false; // completely inert until selected } // Shuffle for (int i = 0; i < allLasers.Count; i++) { int r = Random.Range(i, allLasers.Count); (allLasers[i], allLasers[r]) = (allLasers[r], allLasers[i]); } // Split into 3 batches int batchSize = Mathf.CeilToInt(Mathf.Max(1, allLasers.Count) / 3f); for (int i = 0; i < allLasers.Count; i += batchSize) batches.Add(allLasers.GetRange(i, Mathf.Min(batchSize, allLasers.Count - i))); StartCoroutine(RunBatches()); } IEnumerator RunBatches() { for (int bi = 0; bi < batches.Count; bi++) { // Disable everything first foreach (var lb in allLasers) if (lb) { lb.enabled = false; lb.ResetCycle(); } // Activate this batch activeLasers.Clear(); activeLasers.AddRange(batches[bi]); foreach (var l in activeLasers) { if (!l) continue; l.enabled = true; l.ResetCycle(); l.SetLaserPhase(LaserPhase.Idle); } float batchEnd = Time.time + interval; // Loop phases for the duration of this batch while (Time.time < batchEnd) { // Compute runway required to complete a Charge->Fire sequence float runway = Mathf.Max(0f, chargingWindup) + Mathf.Max(0f, fireDuration); float timeLeft = batchEnd - Time.time; // If not enough time to do Charging+Firing, just stay Idle until the batch ends. if (timeLeft < runway) { yield return PhaseBlock(LaserPhase.Idle, timeLeft, batchEnd); break; } // IDLE float idleTime = Mathf.Max(0f, idleBeforeCharge); // If idleTime itself would consume too much and leave < runway, trim idle to fit if (idleTime > 0f && idleTime > timeLeft - runway) idleTime = Mathf.Max(0f, timeLeft - runway); if (idleTime > 0f) { yield return PhaseBlock(LaserPhase.Idle, idleTime, batchEnd); if (Time.time >= batchEnd) break; } // CHARGING (blink) if (chargingWindup > 0f) { yield return PhaseBlock(LaserPhase.Charging, chargingWindup, batchEnd); // we guaranteed runway, so we should still have time left for Firing } // FIRING (lethal) if (fireDuration > 0f) { yield return PhaseBlock(LaserPhase.Firing, fireDuration, batchEnd); } } } } // Set phase for all active lasers and tick per-frame for 'duration' (clamped by batch end) IEnumerator PhaseBlock(LaserPhase phase, float duration, float batchHardEnd) { SetPhaseForAll(phase); float end = Mathf.Min(Time.time + duration, batchHardEnd); while (Time.time < end) { if (phase == LaserPhase.Charging) { foreach (var l in activeLasers) if (l && l.isActiveAndEnabled) l.TickLaserDuringCharging(); } else if (phase == LaserPhase.Firing) { foreach (var l in activeLasers) if (l && l.isActiveAndEnabled) l.TickLaserDuringFiring(); } yield return null; } } void SetPhaseForAll(LaserPhase p) { foreach (var l in activeLasers) if (l && l.isActiveAndEnabled) l.SetLaserPhase(p); } }