Quick answer: ParticleSystem.Stop() stops emitting by default; live particles finish their lifetime. Use Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear) to remove them instantly. The Stop Action (Disable, Destroy, Callback) runs only once the system has stopped and every particle has died — which never happens for a looping system you never stop.
Two separate features share the word "stop": the Stop() method, which controls emission, and the Main module's Stop Action, which decides what happens to the GameObject afterwards. Most particle clean-up bugs come from expecting one to do the other's job.
Stop() and its behaviours
ps.Stop(); // = Stop(true, ParticleSystemStopBehavior.StopEmitting)
- StopEmitting — no new particles; existing ones live out their lifetime. Smoke trails fade naturally.
- StopEmittingAndClear — stop and remove all particles immediately.
The first argument, withChildren, also stops child systems. Call it on the root of a multi-part effect; stopping only a child leaves the others running.
Stop Action
In the Main module, Stop Action runs after the system is stopped and its last particle has died:
- None — nothing.
- Disable — deactivates the GameObject. Best for pooled effects.
- Destroy — destroys the GameObject. Fine for one-off effects, wrong with pooling.
- Callback — sends
OnParticleSystemStopped()to scripts on the same GameObject.
void OnParticleSystemStopped()
{
pool.Release(gameObject);
}
"My effect is never cleaned up"
- Looping is on. A looping system never stops by itself, so the Stop Action never runs. Turn Looping off for one-shot effects, or call
Stop()when the effect should end. - The callback script is on a different object.
OnParticleSystemStoppedis only sent to the GameObject that has the ParticleSystem. - A child system is still alive. The parent waits for its own particles; children with longer lifetimes keep their own objects alive. Give every system in the effect the same Stop Action, or manage clean-up from the root.
"My particles vanish instantly"
You called Stop(true, StopEmittingAndClear), Clear(), or deactivated the GameObject directly with SetActive(false) — which hides all particles at once. To let them fade, call Stop() and let the Disable stop action deactivate the object afterwards.
Pooling pattern
public void Play(Vector3 pos)
{
transform.position = pos;
gameObject.SetActive(true);
ps.Clear(true);
ps.Play(true);
}
// Main module: Looping off, Stop Action = Disable (or Callback to return to the pool)
Clearing before playing removes particles left over from the previous use.
Checking state
ps.isEmitting tells you whether it is still spawning; ps.IsAlive(true) whether any particles (including children) are still alive; ps.isStopped whether it is fully stopped. Waiting for !ps.IsAlive(true) is the code equivalent of a stop action.