using System;
using UnityEngine;
namespace Watermelon
{
///
/// ParticleCase is responsible for managing individual particle instances, controlling their lifecycle,
/// position, rotation, scale, and handling special behaviors attached to the particle system.
///
public sealed class ParticleCase
{
// Time at which the particle will be disabled. Initialized to -1, meaning no disable time is set.
private float disableTime = -1;
///
/// The ParticleSystem component that controls the visual behavior of the particle.
///
public readonly ParticleSystem ParticleSystem;
///
/// The special behavior script, if any, attached to the particle prefab. Allows custom logic for activation/deactivation.
///
public readonly ParticleBehaviour SpecialBehavior;
///
/// Indicates whether the particle system's parent should be reset (detached) when disabled.
///
public readonly bool ResetParent;
///
/// Event that is invoked when the particle is disabled, allowing for additional cleanup or callbacks.
///
public event SimpleCallback Disabled;
///
/// A flag that indicates if the particle should be forcefully disabled.
///
public bool IsForceStopped { get; private set; }
///
/// Initializes a new instance of the ParticleCase class, setting up the particle object and system.
///
/// The particle object that this case will manage.
/// Determines if the particle should start playing immediately or later (if delayed).
public ParticleCase(ParticleSystem particleSystem, bool isDelayed, bool resetParent)
{
if (particleSystem == null)
{
Debug.LogError("ParticleCase constructor error: 'particleSystem' cannot be null. Please provide a valid ParticleSystem instance.");
return;
}
// Store the particle data
ParticleSystem = particleSystem;
ResetParent = resetParent;
IsForceStopped = false;
// If not delayed, immediately play the particle system
if (!isDelayed)
{
ParticleSystem.Play();
}
// If delayed, stop the particle system until it should start playing
else
{
ParticleSystem.Stop();
}
// Get special behavior component from the pooled object
SpecialBehavior = ParticleSystem.GetComponent();
if (SpecialBehavior != null)
{
// Trigger the special activation behavior
SpecialBehavior.OnParticleActivated();
}
}
///
/// Disables the particle system and handles any special behavior for when the particle is disabled.
///
public void OnDisable()
{
if (ParticleSystem != null)
{
// Detach from any parent object and stop the particle system
if (ResetParent)
ParticleSystem.transform.SetParent(null);
ParticleSystem.Stop();
// Deactivate the particle game object to return it to the pool
ParticleSystem.gameObject.SetActive(false);
}
// If the particle has special behavior, trigger its OnParticleDisabled event
if (SpecialBehavior != null)
{
SpecialBehavior.OnParticleDisabled();
}
Disabled?.Invoke();
}
///
/// Forcefully disables the particle system, with an optional stop behavior (e.g., stopping emission or completely stopping).
///
/// Determines the method for stopping the particle system (e.g., stop emission or completely stop).
public void ForceDisable(ParticleSystemStopBehavior stopBehavior = ParticleSystemStopBehavior.StopEmitting)
{
IsForceStopped = true;
if(ParticleSystem != null)
{
if (ResetParent)
ParticleSystem.transform.SetParent(null);
ParticleSystem.Stop(true, stopBehavior);
}
Disabled?.Invoke();
}
///
/// Applies a custom action to the main particle system and any child particle systems attached to it.
/// This allows for dynamic modifications to the particle systems.
///
/// Example usage:
///
/// particleCase.ApplyToParticles((ParticleSystem particleSystem) =>
/// {
/// ParticleSystem.MainModule main = particleSystem.main; // Access the main module of the particle system
/// main.startColor = Color.red; // Change the start color of the particle to red
/// });
///
/// In this example, the start color of the main particle system and its child particle systems is set to red.
///
/// The action to apply to the particle systems (e.g., change settings, play or stop).
public void ApplyToParticles(Action action)
{
// Apply the action to the main particle system
action?.Invoke(ParticleSystem);
// Get all child particle systems and apply the action to them as well
ParticleSystem[] childSystems = ParticleSystem.GetComponentsInChildren();
foreach (ParticleSystem ps in childSystems)
{
// Avoid applying the action to the main particle system again
if (ps != ParticleSystem)
{
action?.Invoke(ps);
}
}
}
///
/// Checks whether the particle should be forcefully disabled. This could be either due to a forced disable
/// flag being set or because the particle's duration has elapsed.
///
/// True if the particle needs to be disabled, otherwise false.
public bool IsForceDisabledRequired()
{
if (IsForceStopped)
return true;
if (disableTime != -1 && Time.time > disableTime)
return true;
return false;
}
///
/// Sets onDisabled callback that will be invoked when the particle is disabled
///
public ParticleCase SetOnDisabled(SimpleCallback onDisabled)
{
Disabled = onDisabled;
return this;
}
///
/// Sets the position of the particle system in the world.
///
/// The world position to move the particle system to.
/// Returns the current ParticleCase for method chaining.
public ParticleCase SetPosition(Vector3 position)
{
ParticleSystem.transform.position = position;
return this;
}
///
/// Sets the scale of the particle system.
///
/// The scale to apply to the particle system.
/// Returns the current ParticleCase for method chaining.
public ParticleCase SetScale(Vector3 scale)
{
ParticleSystem.transform.localScale = scale;
return this;
}
///
/// Sets the rotation of the particle system.
///
/// The rotation to apply to the particle system.
/// Returns the current ParticleCase for method chaining.
public ParticleCase SetRotation(Quaternion rotation)
{
ParticleSystem.transform.localRotation = rotation;
return this;
}
///
/// Sets the duration after which the particle system should be disabled.
///
/// The duration in seconds for how long the particle system should stay active.
/// Returns the current ParticleCase for method chaining.
public ParticleCase SetDuration(float duration)
{
disableTime = Time.time + duration;
return this;
}
///
/// Sets a target for the particle system to follow.
///
/// The Transform that the particle system should follow.
/// The local position of the particle system relative to the follow target.
/// Returns the current ParticleCase for method chaining.
public ParticleCase SetTarget(Transform followTarget, Vector3 localPosition)
{
ParticleSystem.transform.SetParent(followTarget);
ParticleSystem.transform.localPosition = localPosition;
return this;
}
}
}