SILO27: CRASHLANDED Nice Dice Roller: friendly & customizable design OTHER BLOG POSTS

Using Particle Systems with ECS in Unity

Cover image

This tutorial assumes you are already familiar with Unity itself and the ECS API!

I’ve been upgrading more systems to Unity’s ECS (Entity Component System) for my game. One of the things are these “drones” that shoot projectiles at the player. So I had to figure out how to use particle systems with entities and figured I’d explain how I did it.

Setting up

Lets start by creating a tag component. Tag components are called like that because they contain no data, they are used to “tag” entities by acting like markers. We will add this component to entities we want to emit particles from.
You can learn more about IComponentData in the ECS docs.

using Unity.Entities;

public struct VfxEmitter : IComponentData { }

Then create a new file and set up a basic ECS system. This will be the script in charge of spawning the particles.

using UnityEngine;
using Unity.Entities;
using Unity.Transforms;

public class VfxSystem : SystemBase
{
  protected override void OnCreate()
  {
    base.OnCreate();
    Enabled = false; // Dont run the system until we have set everything up
  }

  protected override void OnUpdate()
  {
    // Empty for now
  }
}

The particles in the scene

Now we can create the actual particle effect. This is done as usual, with a built-in ParticleSystem.
For example here is what I made:

Limitations

Since we’ll need to do the emission of particles by hand there are limitations on the type of effects that can be used. Only looping effects will work. A lot of custom code is needed to recreate specific effects that are not loopable.
Effects that have multiple ParticleSystems (usually as children of the parent effect) can be used with the same limitation of being looping effects. To do it you can just loop over the children and find the ParticleSystems or using GetComponentsInChildren. Then loop over those instead of just using the single one. (The files for Patreon supporters contain a ready script for this).

Make sure PlayOnAwake is off when actually running the game. Otherwise the particles will start spawning immediately and not at the entities’ positions.

screenshot of inspector of particle system with correct settings

Now we need to get a reference to the particle emitter in the scene from the ECS system. This can be done in many different ways, for example using Find or by instantiating it at runtime.
For the purposes of this tutorial we’ll just make a quick and dirty helper scripts that finds the system and passes it our ParticleSystem from the game scene.

using Unity.Entities;
using UnityEngine;

public class VfxSystemHelper : MonoBehaviour
{
    public ParticleSystem particles;

    void Start()
    {
        World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<VfxSystem>().Init(particles);
    }
}

Then we create the function that gets called by the helper script. This function receives the reference and then enables the system to make it start running.

public class VfxSystem : SystemBase
{
    UnityEngine.ParticleSystem particleSystem;
    Transform particleSystemTransform;

    protected override void OnCreate() // ...

    public void Init(UnityEngine.ParticleSystem particleSystem)
    {
      this.particleSystem = particleSystem;
      particleSystemTransform = particleSystem.transform;
      Enabled = true; // Everything is ready, can begin running the system
    }

    protected override void OnUpdate()

Emitting custom particles

And now lets make it actually emit particles!
We’ll do this by using the ParticleSystem.Emit method. For each entity that has our tag component attached, every frame, the system will move the ParticleSystem’s transform to that position and emit some particles.

Note that the entity must have a Translation component (from Unity.Transforms) or some other custom component from which a position can be read, in addition to the VfxEmitter component we created at the start of this tutorial.

protected override void OnUpdate()
{
   Entities.WithAll<VfxEmitter>().ForEach((in Translation translation) =>
   {
      particleSystemTransform.position = translation.Value;
      particleSystem.Emit(1);
   }).WithoutBurst().Run();
}

Frame-rate indipendence

However, since the Emit method takes in a count of how many particles to spawn we could run into an issue: the amount of particles spawned is framerate dependent.
For example, say we are emitting one particle for every time the Update function is executed. Then if the game is running at 30fps that will be 30 particles; but if the game runs at 60 it would spawn 60. And it’s even worse if the framerate is not constant!

We can fix this by emulating a fixed emission rate, similar to how the builtin emission would work. We’ll also read the emission over time rate directly from the effect.

emission over time rate

First, let’s add some variables. interval is time in seconds between each particle spawn and timer is to keep track how much time has passed since the last spawn.

UnityEngine.ParticleSystem particleSystem;
UnityEngine.ParticleSystem.EmitParams emitParams;
float interval;
float timer;

In the Init method we calculate the interval value.
This could also be done every frame if you want to change the emission rate dynamically. We’ll just do it a the start and then use the cached value to save on performance.

particleSystemTransform = particleSystem.transform;
interval = 1f / particleSystem.emission.rateOverTimeMultiplier;
Enabled = true; // Everything is ready, can begin running the system

And in the Update method; we begin by incrementing the timer with the time passed since the last frame.

var deltaTime = (float)Time.DeltaTime;
timer += deltaTime;

From that we calculate how many particles should be spawned this frame. The count will be zero until enough time has passed. Or in case of very high emission rate or very long frame times the count will be larger to account for skipped emissions.

var count = Mathf.RoundToInt(timer / interval);

If the count is more than 0 we emit that amount at every entity’s position.

if (count > 0)
{
    Entities.WithAll<VfxEmitter>().ForEach((in Translation translation) =>
    {
        particleSystemTransform.position = translation.Value;
        particleSystem.Emit(count);
    }).WithoutBurst().Run();
}

And finally (still inside the if (count > 0)) we adjust the timer based on how many particles were spawned.

timer -= count * interval;

Particles with ECS!

And this is the result in game:

Look at them go!

Doing more

You can add a field to the tag component to store things like scale or color for that entity. Then instead of just passing the count to the Emit method you can also pass some EmitParams with your customized fields. Remember to use a shader compatible with ParticleSystems to make sure changing the color for example actually works.

Download files

The Standard package contains the script to put on the entity and the system. The Premium package, instead, also contains a test scene which includes the helper script and also a script that supports child ParticleSystems.

Preview of the example scene

Enjoying the tutorials? Are they useful? Want more?

If you think these posts have either helped or inspired you, please consider supporting this blog.

Help this blog continue existing

If the knowledge you have gained had a significant impact on your project, a mention in the credits would be very appreciated.

Silo27 Logo
Enrico Monese

All around developer, from games and web to weird commandline utilities. 2D/3D art, with a good care for UI/UX and clean design.

Twitter Twitter Patreon Patreon Mastodon Mastodon Blog Blog   Reddit Reddit Github Github Gitlab Gitlab ArtStation ArtStation Instagram Instagram Mail Mail