Niagara Particles: Setting Positions Manually

· Read in about 8 min · (1604 Words)

The Requirement

Niagara is great for simulated particle systems, but its renderers seem pretty useful for non-simulated things as well. What if I just want to give Niagara a set of points, and tell it to use those as particles?

My use case was a “throwing arc” visualisation, for when you’re aiming a thrown projectile. UE provides you with helper functions to generate a list of points from a given start position and velocity (see the “Predict Projectile Path” suite of functions), and I want to display that in the world to preview where I’m throwing it. Something like this (points exaggerated to illustrate):

Throwing visualisation mockup

A Niagara ribbon particle renderer would be perfect for displaying this. I could make my own mesh component or something, but why would I when this is already there? But I don’t want the Niagara system itself to do any simulating; just take my points and don’t mess with them, just render them nice pls.

Beware the Rabbit Holes

This sounds like a simple requirement, but if like me you try to find information about it, you very easily end up falling down rabbit holes such as:

  • Niagara Data Channels: Currently “experimental” so you shouldn’t ship it anyway, and has changed enough already that most of the published information on it is wrong. Also, it’s mostly a kind of “listener” system, spawning particles based on a channel of data, and I don’t really want that - I just want to be able to update a list of points.

  • Niagara Data Interfaces: These sound perfect, except that the docs only tell you how to define them, not how to actually hook them up to a Niagara system. As it turns out, you don’t need to use these directly at all, but you can spend a long time crawling down the APIs without knowing that, squinting in the dark looking for a crack of daylight that tells you how to link these things together.

You don’t need any of this. In fact, you just need regular Niagara exposed user parameters and a bit of non-obvious configuration. I thought that user parameters couldn’t do this, because they’re not per-particle. But there’s a way to hook up an array parameter and get particles to index into it consistently, which effectively lets you set any property of a particle manually.

For more details, read on, adventurer.

Creating the Niagara System

Create an empty Niagara system to begin with. But immediately change the “Loop Behaviour” under “System State” to “Infinite”. You can set the “Loop Duration” to anything you like, I’ve set it to 5 seconds for the sake of it, since this system is going to be pretty static except for point changes, which is handled in code.

Creating the User Parameters

We need 2 parameters to our Niagara system in order to pass a list of points to form our arc visualisation:

  1. The number of points in the arc
  2. A list of positions

In your Niagara system, click on User Parameters, and use the “+” button to add a parameter. For the number of points, just create an int32 User Parameter. For the list of positions, you need a Position Array:

Create a position array

You’ll notice how this is under the category “Data Interface”! So this is how those are hooked up. However we’re not going to make a custom one, just a simple position array will do.

It must be a Position array and not a Vector array, because there are some Large World Coordinate conversions that need to be done for positions (these happen automatically so long as you use Position and not Vector).

Once you’ve set up both, it should look like this:

User parameters list

Creating the Emitter

In a Niagara system, create a new empty emitter. This is our goal:

Emitter

I’ll cover the steps to get there below.

Create the Ribbon Renderer

In this case, we want a Ribbon renderer, so delete or disable the sprite renderer for now. Add a new Ribbon renderer at the bottom, the default settings are fine for now.

Set Persistent Particle IDs

We are going to use “particle ID” information to index into our parameters, so we need those to stay stable across frames. To do this, click on the top-level of the emitter and look for “Requires Persistent IDs”, and turn it on:

Enable persistent IDs

Spawn Particles

We want to spawn the number of particles set in the user parameter. This is simple enough, just create a “Spawn Burst Instantaneous” under “Emitter Update”, then for “Spawn Count”, use the drop-down to pick “ArcPositions” (it’s under “Link Inputs > User”).

Spawn Burst Instantaneous setup

Initialise Particle

On spawn, we want to set the particle positions according to our incoming array of positions. Click on “Initialise Particle” under “Particle Spawn”.

  • Set “Color Mode” to “Direct Set” and just make it white for now
  • Set “Ribbon Width Mode” to “Direct Set” and set it to say 5
  • Change “Position Mode” to “Direct Set” as well, but instead of typing a single value, use the drop-down to the right of the value, and pick “Select Position from Array”

You need a very specific chain of settings for the “Select Position from Array” value. Just follow this, top to bottom, using the search to find the right entries:

Initialise particle setup

It’s a little fiddly, but ultimately you’re telling Niagara to read a position from the user parameter array, using the particle index (part of the Particle ID details) as an index. As you’re working your way down, make sure to change the default “Particles.ID Unique ID” to “Particles.ID Index” to get this result.

If “ArcPositions” doesn’t appear in the drop-down, you can drag & drop it from the User Parameters pane into the Position Array slot.

Particle State

Next, we want to make sure that the particles don’t “die” while the system is running, because we’re using them as a static display that we update manually. So click on “Particle State” under the “Particle Update” section:

  1. Uncheck “Kill Particles When Lifetime Has Elapsed”
  2. Check “Let Infinitely Lived Particles Die When Emitter Deactivates”.
Particle State setup

This ensures that regardless of particle lifetime, while the emitter is active they won’t be killed. However, once you disable the emitter, they’ll die quickly - necessary to prevent our system accumulating new particles forever.

Finally, go back to “Initialise Particle” and set the Lifetime to a very small value, like 0.1:

Particle Lifetime

This means that when the emitter is disabled, all particles will be killed almost immediately.

Testing the Niagara System

To quickly test that this is doing what we want, you can just set the user parameters manually. Let’s set up a 3-point arc by typing this manually into the User Parameters window:

User parameters test

And here’s the result in the Preview window:

Preview window

IMPORTANT: Once you’ve tested this, reset the User Parameters back to their uninitialised state (clear the position list and set count to 0). I’ve seen it cause some odd problems from the real BP code if you don’t do this.

Updating Points from Blueprints

So now, let’s actually use this as intended, and send some points from code. I’ll show a Blueprint example, but you can do this from C++ too of course.

  1. In your parent blueprint (in my case, my character), add a Niagara component
  2. Uncheck “Auto Activate” - we don’t want this activating before we give it parameters
  3. Add a function “Update Arc” or similar, which is where we’ll do our work.

Firstly, let’s generate some points from a predicted projectile path:

BP predict projectile path

We save these points in a variable (local in a function), just so that accessing it multiple times is easier and doesn’t result in the prediction happening more than once.

Next, we want to pass this through to the Niagara User Parameters:

BP implementation

This is slightly more complicated than just setting the positions and count parameters. “Niagara Set Position Array” and “Set Niagara Variable (Int32) are straightforward, but it’s important thing to understand that the number of particles is only set at spawn time, which only happens once on the emitter. So by default, particles will only be spawned (based on ArcPositionsCount) when the system is activated (or reset), or when the Loop Duration is hit.

We want to be able to update the point list whenever we like, and the point count may well change. So, we record the previous count we used, and if it changes, we reset the Niagara system so that the right number of particles are freshly spawned. If we didn’t do that, we could update the parameters, but there would still be the previous number of particles until the Loop Duration expired.

Apart from that wrinkle, everything is pretty straight forward.

In game, it looks something like this:

In game shot

And if we temporarily re-enable the sprite renderer, we can see where the points are:

In game shot

Cleaning Up

While this Niagara system is running, it will be spawning new particles to update the curve. To ensure that these particles are cleaned up if you’re not destroying the system, you should explicitly disable the emitter when you’re done with it. This will trigger the kill of the particles in the system and prevent it increasing forever, if you re-use the system. Just resetting the system is not enough to clear the particles, which will otherwise live forever.

Disable emitter

Conclusion

So there we are: it’s actually not that hard to manually give Niagara points to render, it’s just not very well documented, so that’s why I felt the need to write this article, in the hope I can help someone else not disappear down the various rabbit holes the UE documentation suggests you dive into when you absolutely don’t need to. Hope it’s useful!