I’m routing all of our “splatter” particle systems in through a centralized particle system. I have manually coded fake-emitters that make Emit() calls on the central system, so even though it looks like a bunch of separate particle systems, it’s really all just one.
We’re using it for blood: x.com
… and for smoke: x.com
Neat stuff. It’s all 3D mesh particles (just simple cubes). Reacts to whatever surface it contacts, etc. Anyways!
Right now, both systems just have a standard particle lifetime. That’s fine for smoke, which naturally disappears, but with the blood, it’d be nice if it just hung around until we’d actually emitted enough blood particles to actually need to get rid of some. Then I can set the max size of the blood array based on performance settings, and after an epic combat on a high-end machine potentially the whole dang arena can be coated in the stuff.
Our basic plan looks a bit like this answer: Auto deleting last particle(if limit riched) - Questions & Answers - Unity Discussions
In short, per-frame, do a GetParticles, determine if the array is approaching max size, and if so, loop through the entire array, find the oldest particles, set their lifetimes such that they’ll disappear, and then SetParticles the modified array in. Now the particle system has more gaps to work with, easy.
Since we’d be creating a fixed-size array, GetParticles shouldn’t allocate. So, THEORETICALLY, SetParticles shouldn’t either, and even though we’re looping through 10k-100k particles in a frame, that’s exactly the sort of work that modern CPUs shrug at. So we should be fine.
But.
Those are some big numbers. Has anyone done anything like this before? Any Unity-side employees able to confirm that this all should be fine? The end result should still be substantially more performent than a billion independent particle systems, one would imagine?
On the other end of things, we could break this up to one set of emitters per entity that can bleed/smoke. That would involve substantially more emitters (80+ active at a time, if not more, given that it’s 4 per entity, and that’s assuming a given entity can’t bleed AND smoke, which would double it again). It solves the maximum-count problem, but the drawcalls go through the roof, and given how dispersed an enemy’s blood splatter is likely to be across a large arena space, it isn’t like the independent emitters would be any more likely to be visually culled when out of view.
The structure of our game is moving between large combat arenas, so it’s probable we’d clear all blood/smoke/etc as you loaded between arenas. Within any given arena, the sight lines are such that beyond simple view frustum culling, there’s nothing to be gained from attempting to cull the blood/smoke/etc more aggressively. We’d also have to make the frustum VERY wide even then, to make sure emissions happening off-screen near us actually take place, given that the game’s first person melee.