Creating sound for particle collisions with Niagara


The effect in this screen shot probably took a day or two of game jam time! 


Spawning the stalactites:
Used the recently added procedural foliage volume tool to spawn 5 types of foliage (one for each color) 
The foliage itself is a static mesh, with a strong emissive rim lighting.

Spawning particle systems: 

Then,  we find the location of each foliage instance, and add a Niagara actor to the foliage instance transform, plus an offset defined by a ParticleSpawn socket that's a static mesh property. In the future, this could be changed to have multiple particle effect spawn points per instance, or none at all. We also seed the Niagara effect with the color of the stalactite.
Further optimization would be possible  - e.g. could a single niagara emitter be configured to have an array of particle spawn points?
Also, in my case - the spawn actors are built at game start - probably have instead had an editor function for it (e.g. subclassed procedural foliage volume), so that every time the procedural foliage is regenerated, we also  triggered regenerating the Niagara actor/s. 

The effect itself has three emitters, a static mesh emitter for the droplet (perhaps this would have looked better with a rim emission effect similiar to the stalactite itself?) A ribbon effect for the trail, and a splash burst for the collision. 

Getting data back from Niagara:
Because I wanted to play a sound (and increment a counter in game logic) every time a particle strikes a location, I needed a way to bring particle collision information back to the game thread. 
Fortunately, UNiagaraDataInterfaceExport exists - which can be used from within a Niagara module to notify a blueprint. While it was relatively easy to export all droplet mesh particle locations back - this didn't seemed to perform so well (this was  especially significant in my test level - where with 100s of active systems, framerates dropped precipitously).

Custom module for sending collision data back to blueprints

However, if I killed off a particle immediately when it collided, there didn't seem to be a way to run it through my custom module!   My work around was to just use the age multiplication setting within the Niagara collision module to make the colliding particle just age at 100x speed (effectively casuing it to despawn one frame after colliding. I could then check for 'hasCollided' in my event module to just send back the location of particles that had collided within the last frame.  Once we have the collision location in blueprint, it's trivial to find out what it hit based on it's velocity and a raycast  (in my case I can just use world down, but you could instead send along previous frame velocity in the event, which should allow for better identifying which object was hit). By limiting it to only send back particle data just on recently colliding particles instead of all particles, fps jumped back up to 60.

I hadn't run across any tutorials/videos describing this specific technique (Perhaps I  should put one up?).  I think in general, using this approach to play a sound makes sense. In some niche scenerios (e.g. games with no networking component) this is also fine for gameplay - and it might be especially effective in a bullet hell style game. There are likely other ways of getting at the same effect that would allow for networking, but I would think most would limit your use of niagaras spawning features in some way. Also, this has been possible for some time by writing a custom data interface - but there might be some extra utility here as it's a blueprint only solution. 

Playing with sound:
Because each NiagaraActor knows the color it seeded the particle system with, we select which sound to play based on color. Higher frequency colors are associated with higher frequency sounds. In an earlier draft,  I had tried pitch shifting an individual sound so that increases in color frequency directly mapped to increases in pitch (i.e. mapping an octave of sound to an octave of light). 
I could probably have run a bit more with this, including multiple samples - but ultimately just ended up playing notes on the pentatonic scale and using LMMS to generate marimba/vibraphone-esque sounds corresponding to each color (still increasing in frequency as color increases, but not a 1 to 1 mapping).

In the future, I might like to take this system and have it map to 12-tone-equal temperament notes, and combine it with more strategically placed emitters - allowing for procedurally generated arpeggios/scales as the player runs by.


Note:

You'll also note the rather perfunctory 'If' in the module that's setting particle position equal to.. the particles current position. That's due to a https://en.wikipedia.org/wiki/Heisenbug where adding some output for a particle seemed to result in correct behavior. My assumption was that behind the scenes my module was getting optimized away unless it actually performed an operation that updated a referenced property - since nothing depended on Output Module Success, the whole thing was considered a no-op?

Get Persephone's Party

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.