| | | 1 | | using System.Collections; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using UnityEngine; |
| | | 4 | | |
| | | 5 | | public class ParticleManager : MonoBehaviour { |
| | 2 | 6 | | public static ParticleManager Instance { get; private set; } |
| | | 7 | | |
| | | 8 | | private Queue<GameObject> _missileTrailPool; |
| | | 9 | | [SerializeField] |
| | | 10 | | private int _missileTrailPoolCount; |
| | | 11 | | [SerializeField] |
| | | 12 | | private Queue<GameObject> _missileExplosionPool; |
| | | 13 | | [SerializeField] |
| | | 14 | | private int _missileExplosionPoolCount; |
| | | 15 | | |
| | | 16 | | [SerializeField] |
| | 2 | 17 | | private List<TrailRenderer> _agentTrailRenderers = new List<TrailRenderer>(); |
| | | 18 | | |
| | | 19 | | [SerializeField, Tooltip("The material to use for commandeered interceptor trail renderers")] |
| | | 20 | | public Material InterceptorTrailMatFaded; |
| | | 21 | | [SerializeField, Tooltip("The material to use for commandeered threat trail renderers")] |
| | | 22 | | public Material ThreatTrailMatFaded; |
| | | 23 | | |
| | | 24 | | [SerializeField] |
| | 2 | 25 | | private List<GameObject> _hitMarkerList = new List<GameObject>(); |
| | | 26 | | |
| | 0 | 27 | | public void ClearHitMarkers() { |
| | 0 | 28 | | foreach (var hitMarker in _hitMarkerList) { |
| | 0 | 29 | | Destroy(hitMarker); |
| | 0 | 30 | | } |
| | 0 | 31 | | _hitMarkerList.Clear(); |
| | 0 | 32 | | } |
| | | 33 | | |
| | 1 | 34 | | private void Awake() { |
| | 1 | 35 | | if (Instance != null && Instance != this) { |
| | 0 | 36 | | Destroy(gameObject); |
| | 0 | 37 | | return; |
| | | 38 | | } |
| | 1 | 39 | | Instance = this; |
| | 1 | 40 | | DontDestroyOnLoad(gameObject); |
| | 1 | 41 | | } |
| | | 42 | | |
| | 1 | 43 | | private void Start() { |
| | 1 | 44 | | _missileTrailPool = new Queue<GameObject>(); |
| | 1 | 45 | | _missileExplosionPool = new Queue<GameObject>(); |
| | 1 | 46 | | _hitMarkerList = new List<GameObject>(); |
| | | 47 | | |
| | 1 | 48 | | if (SimManager.Instance.SimulatorConfig.EnableMissileTrailEffect) { |
| | 0 | 49 | | InitializeMissileTrailParticlePool(); |
| | 0 | 50 | | } |
| | 1 | 51 | | if (SimManager.Instance.SimulatorConfig.EnableExplosionEffect) { |
| | 0 | 52 | | InitializeMissileExplosionParticlePool(); |
| | 0 | 53 | | } |
| | | 54 | | |
| | | 55 | | // Subscribe to events. |
| | 1 | 56 | | SimManager.Instance.OnNewInterceptor += RegisterNewInterceptor; |
| | 1 | 57 | | SimManager.Instance.OnNewThreat += RegisterNewThreat; |
| | | 58 | | |
| | 1 | 59 | | SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded; |
| | 1 | 60 | | } |
| | | 61 | | |
| | 33 | 62 | | private void Update() { |
| | 33 | 63 | | _missileTrailPoolCount = _missileTrailPool.Count; |
| | 33 | 64 | | _missileExplosionPoolCount = _missileExplosionPool.Count; |
| | 33 | 65 | | } |
| | | 66 | | |
| | 0 | 67 | | private void InitializeMissileTrailParticlePool() { |
| | | 68 | | // Grab from Resources/Prefabs/Effects. |
| | 0 | 69 | | GameObject missileTrailPrefab = |
| | | 70 | | Resources.Load<GameObject>("Prefabs/Effects/InterceptorSmokeEffect"); |
| | | 71 | | |
| | | 72 | | // Pre-instantiate 10 missile trail particles. |
| | 0 | 73 | | for (int i = 0; i < 10; ++i) { |
| | 0 | 74 | | InstantiateMissileTrail(missileTrailPrefab); |
| | 0 | 75 | | } |
| | | 76 | | // Instantiate over an interval. |
| | 0 | 77 | | StartCoroutine(InstantiateMissileTrailsOverTime(missileTrailPrefab, 200, 0.05f)); |
| | 0 | 78 | | } |
| | | 79 | | |
| | 0 | 80 | | private GameObject InstantiateMissileTrail(GameObject prefab) { |
| | 0 | 81 | | GameObject trail = Instantiate(prefab, transform); |
| | 0 | 82 | | trail.GetComponent<ParticleSystem>().Stop(); |
| | 0 | 83 | | _missileTrailPool.Enqueue(trail); |
| | 0 | 84 | | return trail; |
| | 0 | 85 | | } |
| | | 86 | | |
| | | 87 | | private IEnumerator InstantiateMissileTrailsOverTime(GameObject prefab, int count, |
| | 0 | 88 | | float duration) { |
| | 0 | 89 | | float interval = duration / count; |
| | 0 | 90 | | for (int i = 0; i < count; ++i) { |
| | 0 | 91 | | InstantiateMissileTrail(prefab); |
| | 0 | 92 | | yield return new WaitForSeconds(interval); |
| | 0 | 93 | | } |
| | 0 | 94 | | } |
| | | 95 | | |
| | 0 | 96 | | private void InitializeMissileExplosionParticlePool() { |
| | 0 | 97 | | GameObject missileExplosionPrefab = |
| | | 98 | | Resources.Load<GameObject>("Prefabs/Effects/InterceptExplosionEffect"); |
| | | 99 | | // Pre-instantiate 10 missile trail particles. |
| | 0 | 100 | | for (int i = 0; i < 10; ++i) { |
| | 0 | 101 | | InstantiateMissileExplosion(missileExplosionPrefab); |
| | 0 | 102 | | } |
| | 0 | 103 | | StartCoroutine(InstantiateMissileExplosionsOverTime(missileExplosionPrefab, 200, 0.05f)); |
| | 0 | 104 | | } |
| | | 105 | | |
| | 0 | 106 | | private GameObject InstantiateMissileExplosion(GameObject prefab) { |
| | 0 | 107 | | GameObject explosion = Instantiate(prefab, transform); |
| | 0 | 108 | | explosion.GetComponent<ParticleSystem>().Stop(); |
| | 0 | 109 | | _missileExplosionPool.Enqueue(explosion); |
| | 0 | 110 | | return explosion; |
| | 0 | 111 | | } |
| | | 112 | | |
| | | 113 | | private IEnumerator InstantiateMissileExplosionsOverTime(GameObject prefab, int count, |
| | 0 | 114 | | float duration) { |
| | 0 | 115 | | float interval = duration / count; |
| | 0 | 116 | | for (int i = 0; i < count; ++i) { |
| | 0 | 117 | | InstantiateMissileExplosion(prefab); |
| | 0 | 118 | | yield return new WaitForSeconds(interval); |
| | 0 | 119 | | } |
| | 0 | 120 | | } |
| | | 121 | | |
| | 14 | 122 | | private void RegisterNewInterceptor(IInterceptor interceptor) { |
| | 14 | 123 | | interceptor.OnHit += RegisterInterceptorHit; |
| | 14 | 124 | | interceptor.OnMiss += RegisterInterceptorMiss; |
| | 14 | 125 | | interceptor.OnTerminated += RegisterAgentTerminated; |
| | 14 | 126 | | } |
| | | 127 | | |
| | 0 | 128 | | private void RegisterInterceptorHit(IInterceptor interceptor) { |
| | 0 | 129 | | if (SimManager.Instance.SimulatorConfig.EnableExplosionEffect) { |
| | 0 | 130 | | PlayMissileExplosion(interceptor.Position); |
| | 0 | 131 | | } |
| | 0 | 132 | | GameObject hitMarkerObject = SpawnHitMarker(interceptor.Position); |
| | 0 | 133 | | hitMarkerObject.GetComponent<UIEventMarker>().SetEventHit(); |
| | 0 | 134 | | _hitMarkerList.Add(hitMarkerObject); |
| | 0 | 135 | | } |
| | | 136 | | |
| | 0 | 137 | | private void RegisterInterceptorMiss(IInterceptor interceptor) { |
| | 0 | 138 | | GameObject hitMarkerObject = SpawnHitMarker(interceptor.Position); |
| | 0 | 139 | | hitMarkerObject.GetComponent<UIEventMarker>().SetEventMiss(); |
| | 0 | 140 | | _hitMarkerList.Add(hitMarkerObject); |
| | 0 | 141 | | } |
| | | 142 | | |
| | 955 | 143 | | private void RegisterNewThreat(IThreat threat) { |
| | 955 | 144 | | threat.OnTerminated += RegisterAgentTerminated; |
| | 955 | 145 | | } |
| | | 146 | | |
| | 0 | 147 | | private void RegisterAgentTerminated(IAgent agent) { |
| | 0 | 148 | | if (SimManager.Instance.SimulatorConfig.PersistentFlightTrails) { |
| | 0 | 149 | | CommandeerAgentTrailRenderer(agent); |
| | 0 | 150 | | } |
| | 0 | 151 | | } |
| | | 152 | | |
| | 11 | 153 | | private void RegisterSimulationEnded() { |
| | 33 | 154 | | foreach (var trailRenderer in _agentTrailRenderers) { |
| | 0 | 155 | | Destroy(trailRenderer.gameObject); |
| | 0 | 156 | | } |
| | 11 | 157 | | _agentTrailRenderers.Clear(); |
| | 11 | 158 | | } |
| | | 159 | | |
| | 0 | 160 | | private GameObject SpawnHitMarker(in Vector3 position) { |
| | 0 | 161 | | GameObject hitMarker = |
| | | 162 | | Instantiate(Resources.Load<GameObject>("Prefabs/HitMarker"), position, Quaternion.identity); |
| | 0 | 163 | | _hitMarkerList.Add(hitMarker); |
| | 0 | 164 | | return hitMarker; |
| | 0 | 165 | | } |
| | | 166 | | |
| | 0 | 167 | | private GameObject PlayMissileExplosion(in Vector3 position) { |
| | 0 | 168 | | if (_missileExplosionPool.Count > 0) { |
| | 0 | 169 | | GameObject explosion = _missileExplosionPool.Dequeue(); |
| | 0 | 170 | | explosion.transform.position = position; |
| | | 171 | | |
| | 0 | 172 | | ParticleSystem particleSystem = explosion.GetComponent<ParticleSystem>(); |
| | 0 | 173 | | if (particleSystem != null) { |
| | 0 | 174 | | particleSystem.Clear(); |
| | 0 | 175 | | particleSystem.Play(); |
| | 0 | 176 | | StartCoroutine(ReturnExplosionAfterDelay(explosion, particleSystem.main.duration)); |
| | 0 | 177 | | } else { |
| | 0 | 178 | | Debug.LogError("Missile explosion particle has no ParticleSystem component."); |
| | 0 | 179 | | _missileExplosionPool.Enqueue(explosion); |
| | 0 | 180 | | return null; |
| | | 181 | | } |
| | | 182 | | |
| | 0 | 183 | | return explosion; |
| | | 184 | | } |
| | 0 | 185 | | return null; |
| | 0 | 186 | | } |
| | | 187 | | |
| | 0 | 188 | | private IEnumerator ReturnExplosionAfterDelay(GameObject explosion, float delay) { |
| | 0 | 189 | | yield return new WaitForSeconds(delay); |
| | 0 | 190 | | ReturnMissileExplosionParticle(explosion); |
| | 0 | 191 | | } |
| | | 192 | | |
| | 0 | 193 | | private void ReturnMissileExplosionParticle(GameObject explosion) { |
| | 0 | 194 | | if (explosion == null) { |
| | 0 | 195 | | Debug.LogError("Attempted to return a null missile explosion particle."); |
| | 0 | 196 | | return; |
| | | 197 | | } |
| | | 198 | | |
| | 0 | 199 | | explosion.transform.parent = transform; |
| | 0 | 200 | | explosion.transform.localPosition = Vector3.zero; |
| | 0 | 201 | | ParticleSystem particleSystem = explosion.GetComponent<ParticleSystem>(); |
| | 0 | 202 | | if (particleSystem != null) { |
| | 0 | 203 | | particleSystem.Stop(); |
| | 0 | 204 | | particleSystem.Clear(); |
| | 0 | 205 | | } else { |
| | 0 | 206 | | Debug.LogError("Attempted to return a missile explosion particle with no particle system."); |
| | 0 | 207 | | } |
| | | 208 | | |
| | 0 | 209 | | _missileExplosionPool.Enqueue(explosion); |
| | 0 | 210 | | } |
| | | 211 | | |
| | 0 | 212 | | private void CommandeerAgentTrailRenderer(IAgent agent) { |
| | | 213 | | // Take the TrailRenderer component off of the agent, so it can be destroyed at the end of the |
| | | 214 | | // simulation. |
| | 0 | 215 | | TrailRenderer trailRenderer = agent.gameObject.GetComponentInChildren<TrailRenderer>(); |
| | 0 | 216 | | if (trailRenderer != null) { |
| | 0 | 217 | | trailRenderer.transform.parent = transform; |
| | 0 | 218 | | _agentTrailRenderers.Add(trailRenderer); |
| | 0 | 219 | | trailRenderer.material = (agent is IThreat) ? ThreatTrailMatFaded : InterceptorTrailMatFaded; |
| | 0 | 220 | | } else { |
| | 0 | 221 | | Debug.LogWarning("Agent has no TrailRenderer component."); |
| | 0 | 222 | | } |
| | 0 | 223 | | } |
| | | 224 | | } |