We've uploaded a generalized version of our batched particle system to our gists repository on GitHub.

There are two components, the BatchedParticleSystem and the BatchedParticleEmitter. The BatchedParticleSystem component is attached to the same object as the new singleton particle system. The BatchedParticleEmitter component is then attached to any object that you would like to emit particles in that system, that is, any object which previously would have had its own instance of the particle system. Then, all of the emitters are added to a list in the batched system, and the emitters are tweaked to behave as desired.

At run-time, the system component will iterate through each emitter and determine, based on the the emission rate defined in the emitter, whether it is time to emit a new particle. If so, it gets the emission parameters from the emitter, and triggers the emission in the particle system. The generalized system we've shown here emits particles over time based on emission rate (just like the default settings of the regular particle system), but it is trivial to expand to emit particles in scheduled bursts, or in response to conditions or other gameplay elements.

When batching multiple particle systems together, its very easy to run in to the default 1000 max particle limit. Make sure to increase that limit in your batched particle system if your emitters are not working properly.

Performance

In the above example, we had 660 instances of a smoke effect and 660 instances of a fire effect, all running at 45 fps. We were able to combine all instances of the smoke and all instances of the fire into batched systems, leaving us with two total systems and 70 fps. Note that this test was run on a high end desktop with a 1080ti. Lower end platforms will benefit from this optimization at a lower number of particle systems. For example, we ran a scaled down test on an Oculus Go, and the numbers were 24 particle systems at 35 fps and 2 particle systems at 70 fps.