Recoloring and Fading Particle Systems in Unity3D on the Fly
Particle systems are fantastic for all kinds of effects in games, including fire, explosions, smoke, water, lighting, galaxy simulations, and much more. Unity comes with a built-in ParticleSystem library that can be used to implement these effects in your 2D or 3D Unity game, and while the Unity implementation is extremely flexible and allows you to create amazing effects, sometimes you’ll have situations that call for some customization that doesn’t come built-in.
For instance, recently I was trying to accomplish a particle effect that would fan out like an explosion, pause in place, and then slowly fade out over time. While fading particles during their lifespan can be accomplished via the built-in editor, waiting for them to be paused first cannot.
Here’s a look at the particle system I was using, which was created using the built-in Particle Editor in Unity:
Pausing the Particle System
As you can see, it begins by fanning out particles in random directions, and then pauses. The pause effect is rather straightforward, and is accomplished by attaching a script to the particle system and pausing after a duration:
public class ParticleScript : MonoBehaviour { public float timeToPause = 2.0f; private ParticleSystem system; private float time; void Awake() { system = GetComponent<ParticleSystem>(); } void Update() { time += Time.deltaTime; if (!system.isPaused && time > timeToPause) { system.Pause(); } } }
We start by defining a public float called timeToPause that indicates how long the particle should remain playing before being paused. In Update, we update time with the Time.deltaTime and pause the particle system using Pause() on the particle system itself.
This is all well and good, but we still need to modify the particle colors after pausing. The idea is that once the system is paused, it begins to fade out over time until it is no longer visible.
Recoloring Particles
What we’ll do is recolor each individual particle separately, since they may not all have the same color depending on how your particle system is configured. In this case all we’ll be modifying is the alpha of the particle to have it fade out of view, but you can apply the same technique to any component of the color as well.
The first thing to do is to get a reference to the current particles in the system when we pause:
public class ParticleScript : MonoBehaviour { ... private ParticleSystem.Particle[] particles; ... void Update() { time += Time.deltaTime; if (!system.isPaused && time > timeToPause) { system.Pause(); particles = new ParticleSystem.Particle[system.particleCount]; system.GetParticles(particles); } } }
We define an array of Particle objects that we’ll use to store the particles when we pause the system. We then initialize it with the current number of particles in the system using system.particleCount to act as a buffer, and then proceed to have the particles stored into the buffer array using system.GetParticles.
Now we can iterate over the particles and modify them accordingly. In Update, we’ll add some logic when the system is paused to recolor the particles like so:
public class ParticleScript : MonoBehaviour { ... public float lifespan = 5.0f; ... void Update() { ... if (particles != null) { for (int p = 0; p < particles.Length; p++) { Color color = particles[p].startColor; color.a = ((lifespan - time + timeToPause) / lifespan); particles[p].startColor = color; } system.SetParticles(particles, particles.Length); } } }
We start by ensuring the system has already been paused and the particles have been initialized, and then iterate over the particles we cached when we actually paused the system. For each one, we grab it’s current color, and update the alpha as a percentage of time that has elapsed compared to the total time (lifespan) we’ve defined. It’s also important to note that we ignore the timeToPause adding it to the time prior to determining how much “fade time” has elapsed.
This will smoothly fade the particles out over time like so:
Performance
Depending on the number of these particle systems you’re fading out, and the number of particles in each system, there are a number of issues with the basic implementation above that you may want to address. For instance, you may not want to update the particles every single frame, and instead only update every 5th frame for example. Additionally, you could likely cache the current color and only update the particles when the change is significant - for instance, don’t update all particles in the system when the difference in alpha is so minor that it likely won’t even be noticeable.
You’ll also want to Destroy the system once it’s fully faded out of view (color.a <= 0f) rather than leaving it laying around.
The performance improvements you choose to implement, if any, will depend on your use case but hopefully this has given you some inspiration for some effects you can implement in your own game!
Full Script
And finally, here’s the full script just in case anything was unclear while following along:
using UnityEngine; using System.Collections; public class ParticleScript : MonoBehaviour { public float lifespan = 5.0f; public float timeToPause = 2.0f; private ParticleSystem.Particle[] particles; private ParticleSystem system; private float time; void Awake() { system = GetComponent<ParticleSystem>(); } void Update() { time += Time.deltaTime; if (!system.isPaused && time > timeToPause) { system.Pause(); particles = new ParticleSystem.Particle[system.particleCount]; system.GetParticles(particles); } if (particles != null) { for (int p = 0; p < particles.Length; p++) { Color color = particles[p].startColor; color.a = ((lifespan - time + timeToPause) / lifespan); particles[p].startColor = color; } system.SetParticles(particles, particles.Length); } } }