using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Watermelon { public class ParticlesController : MonoBehaviour { // Array of particles to be registered and managed. [SerializeField] Particle[] particles; // Dictionary to register particles by their hash code for quick access. private static Dictionary registerParticles = new Dictionary(); // List to hold currently active particle cases. private static List activeParticles = new List(); // List to manage particles that are activated with a delay. private static List delayedParticles = new List(); /// /// Initializes the particles controller by registering all particles in the array. /// Also starts a coroutine to continuously check for active particles. /// public void Init() { // Register particles from the array. for (int i = 0; i < particles.Length; i++) { RegisterParticle(particles[i]); } // Start the coroutine to monitor active particles. StartCoroutine(CheckForActiveParticles()); } /// /// Clears all active and delayed particles, stopping them and removing from lists. /// private void OnDestroy() { // Clear all delayed particles. delayedParticles.Clear(); // Remove all active particles. activeParticles.Clear(); // Destroy pool objects foreach (Particle particle in registerParticles.Values) { particle.Destroy(); } registerParticles.Clear(); } /// /// Updates delayed particles and activates them when their delay has passed. /// private void Update() { // Check delayed particles and activate them when ready for (int i = delayedParticles.Count - 1; i >= 0; i--) { if (Time.time >= delayedParticles[i].ActivationTime) { DelayedParticle delayedParticle = delayedParticles[i]; // Play the particle system delayedParticle.ParticleCase.ParticleSystem.Play(); // Add to active particles list activeParticles.Add(delayedParticle.ParticleCase); // Remove from delayed list delayedParticles.RemoveAt(i); } } } /// /// Coroutine to check for active particles and manage their lifecycle. /// private IEnumerator CheckForActiveParticles() { while (true) { // Wait for several frames to allow for updates. yield return null; yield return null; yield return null; yield return null; yield return null; yield return null; yield return null; // Loop through active particles in reverse to avoid index issues during removal. for (int i = activeParticles.Count - 1; i >= 0; i--) { // Check if the particle case is still valid. if (activeParticles[i] != null && activeParticles[i].ParticleSystem != null) { // If the particle requires forced disable, stop it. if (activeParticles[i].IsForceDisabledRequired()) { activeParticles[i].ParticleSystem.Stop(); activeParticles.RemoveAt(i); continue; } // If the particle system is not alive, disable and remove it. if (!activeParticles[i].ParticleSystem.IsAlive(true)) { activeParticles[i].OnDisable(); activeParticles.RemoveAt(i); } } else { // If the particle case is null, remove it from the active list. activeParticles.RemoveAt(i); } } } } /// /// Activates a particle with an optional delay. /// /// The particle to activate. /// The delay before activation. /// A ParticleCase representing the activated particle. private static ParticleCase ActivateParticle(Particle particle, float delay = 0) { // Create or retrieve the pooled particle object GameObject particleObject = particle.ParticlePool.GetPooledObject(); // Get the ParticleSystem component from the particle object ParticleSystem particleSystem = particleObject.GetComponent(); return ActivateParticle(particleSystem, delay, true); } /// /// Activates a particle with an optional delay. /// /// The ParticleSystem to activate. /// The delay before activation. /// A ParticleCase representing the activated particle. private static ParticleCase ActivateParticle(ParticleSystem particleSystem, float delay = 0, bool resetParent = true) { bool isDelayed = delay > 0; // Activate game object particleSystem.gameObject.SetActive(true); // Create a new ParticleCase for the activated particle. ParticleCase particleCase = new ParticleCase(particleSystem, isDelayed, resetParent); // If delayed, add to delayed particles list for Update to handle. if (isDelayed) { DelayedParticle delayedParticle = new DelayedParticle { ParticleCase = particleCase, ActivationTime = Time.time + delay }; delayedParticles.Add(delayedParticle); return particleCase; } // Immediately add the active particle case to the list. activeParticles.Add(particleCase); return particleCase; } #region Register /// /// Registers a particle, ensuring it has a valid name and prefab. /// /// The particle to register. /// An integer hash code for the registered particle, or -1 if registration fails. public static int RegisterParticle(Particle particle) { // Validate particle name. if (string.IsNullOrEmpty(particle.ParticleName)) { Debug.LogError("[Particle Controller]: Particle can't be initialized with empty name!"); // Registration failed. return -1; } // Validate particle prefab. if (particle.ParticlePrefab == null) { Debug.LogError("[Particle Controller]: Particle can't be initialized without linked prefab!"); // Registration failed. return -1; } // Get the hash of the particle name. int particleHash = particle.ParticleName.GetHashCode(); if (!registerParticles.ContainsKey(particleHash)) { // Initialize the particle. particle.Init(); // Add to the registration dictionary. registerParticles.Add(particleHash, particle); // Return the particle hash code. return particleHash; } else { Debug.LogError($"[Particle Controller]: Particle with name {particle.ParticleName} already register!"); } // Registration failed. return -1; } /// /// Registers a particle by name and prefab. /// /// The name of the particle. /// The prefab associated with the particle. /// An integer hash code for the registered particle. public static int RegisterParticle(string particleName, GameObject particlePrefab) { return RegisterParticle(new Particle(particleName, particlePrefab)); } #endregion #region Play /// /// Plays a particle by its name with an optional delay. /// /// The name of the particle to play. /// The delay before playing the particle. /// A ParticleCase representing the played particle. public static ParticleCase PlayParticle(string particleName, float delay = 0) { // Get the hash of the particle name. int particleHash = particleName.GetHashCode(); // Check if the particle is registered and activate it. if (registerParticles.ContainsKey(particleHash)) { return ActivateParticle(registerParticles[particleHash], delay); } Debug.LogError($"[Particles System]: Particle with type {particleName} is missing!"); // Particle not found. return null; } /// /// Plays a particle by its hash with an optional delay. /// /// The hash of the particle to play. /// The delay before playing the particle. /// A ParticleCase representing the played particle. public static ParticleCase PlayParticle(int particleHash, float delay = 0) { // Check if the particle is registered and activate it. if (registerParticles.ContainsKey(particleHash)) { return ActivateParticle(registerParticles[particleHash], delay); } Debug.LogError($"[Particles System]: Particle with hash {particleHash} is missing!"); // Particle not found. return null; } /// /// Plays a particle instance directly with an optional delay. /// /// The particle instance to play. /// The delay before playing the particle. /// A ParticleCase representing the played particle. public static ParticleCase PlayParticle(Particle particle, float delay = 0) { if (particle == null) { Debug.LogError("PlayParticle error: 'particle' cannot be null. Please provide a valid Particle instance."); return null; } return ActivateParticle(particle, delay); } /// /// Initiates playback of a specified particle instance, allowing for an optional delay. /// /// The ParticleSystem instance to be played. /// The duration (in seconds) to wait before playing the particle. Default is 0. /// A ParticleCase representing the activated particle, or null if the operation failed. public static ParticleCase PlayParticle(ParticleSystem particleSystem, float delay = 0) { if (particleSystem == null) { Debug.LogError("PlayParticle error: 'particleSystem' cannot be null. Please provide a valid ParticleSystem instance."); return null; } return ActivateParticle(particleSystem, delay, false); } #endregion /// /// Checks if a particle with the specified name exists in the particle registry. /// /// The name of the particle to check. /// Returns true if the particle exists in the registry, otherwise false. public static bool HasParticle(string particleName) { // Get the hash of the particle name. int particleHash = particleName.GetHashCode(); return registerParticles.ContainsKey(particleHash); } /// /// Checks if a particle with the specified hash exists in the particle registry. /// /// The hash of the particle to check. /// Returns true if the particle exists in the registry, otherwise false. public static bool HasParticle(int particleHash) { return registerParticles.ContainsKey(particleHash); } // Structure to store delayed particle information private struct DelayedParticle { public ParticleCase ParticleCase; public float ActivationTime; } } }