[Niagara 5.0] Fix ribbon gap Mini Tutorial

Fix ribbon gap


The intention of these mini tutorials is to have easily searchable reference for the implementation of basic ideas in Niagara.

Today I want to have a quick look at how we might solve a common issue when using Ribbons.
Unless you spawn a particle every single frame, there will always be a gap between the source of the ribbon and the ribbon itself.

Spawning a particle every frame, can be a solution in some cases, but it’s often better to find a solution in which you can bridge that gap
There are a few strategies to deal with this, but I’d like to present a fairly basic one that will work in a large number of situations.

Basic Setup
The strategy here will be to reliably find the last spawned particle and snap it to our source location (the emitter position in this example).
We do this by, for every particle, comparing the index of that particle with the total number of particles that have been spawned in the emitter. In the case that we do have the last spawned particle, we set the Position of the particle to the simulation position.

Now to know the total number of particles we still need to keep track of that somehow.
To do this, we’ll adjust the SpawnPerUnit and SpawnRate modules. (You’ll probably want to create a copy for this.)
At the end of the modules we can add a little section that reads the spawn count and adds it to Emitter.TotalParticlesSpawned.

And boom now the last particle of your ribbon will remain attached to the source.

Except that’s not entirely the case.
Due to tick order it’s possible that this script will be executed before the movement script of the source.
This can create a one frame gap between the last particle and the source.
We can solve this on a case by case basis, by forcing the Niagara Tick to later in the frame.
This is generally ill advised because it takes away the engines ability to make use of threading in an efficient way, but if used sparingly it can be helpful.

And there you go, a reasonably robust way to make your ribbons gap-less.



After GPU Ribbon is available to Niagara I’m no longer too bothered with this issue, but I still want to share a less accurate but slightly easier solution to the gap issue when using Spawn Rate.

This approach assumes the user to stick with a constant spawn rate value, because there is very little point of randomizing the spawn rate value every frame in a ribbon emitter. Anyway because the spawn rate value is constant, we can simply calculate the spawn interval in second by 1/SpawnRateValue, then in Particle Update stage, do a Lerp Position and choose between SimulationPosition and Particles.Initial.Position (or just Particles.Position depends on your use case) by checking if the Particles.Age is smaller than the spawn interval.


@ifurkend Funny enough, someone asking how to do your described method for spawn per unit, is what triggered me to look for the presented method.

1 Like

If we are talking about the same guy, he actually wanted the newest particle to go beyond the actor position, which is possible with our methods, but it saddens me that it no longer matters now (not because of the GPU ribbon).

This is actually the same method as Partikel’s tip right, just not done in scratchpad :smiley:
and thanks for the great tip as well Niels!

1 Like

I have a question, because I am unable to read what is before this (or more like they’re too advanced!), what does SpawnOutputInfo return? To my understanding, it seems you’re taking out the every count of particle spawn and accumulate them to the Emitter.TotalParticlesSpawned.
It would be great if you could explain more about this part, thank you.

it seems you’re taking out the every count of particle spawn and accumulate them to the Emitter.TotalParticlesSpawned.

Yup, that’s exactly what we are doing.

Spawn info is the structure a spawn module passes onto the engine to indicate that it wants to spawn new particles.
If you open up one of the epic provided spawn modules, you’ll notice that the last node in those modules is the one I marked in white.

So we simply copy the epic provide modules and put those nodes in the picture behind it to check how many particles this modules wants to spawn and add it to our count.

1 Like

Hello Niels!

I tried your tutorial but for the second part (fixing the spawn rate or spawn per unit) when compiling the module script it tells me there is errors :

I’m not sure what to do since two of them are located on nodes that came from the basic spawn rate module. The last one is the map set at the end (the one you add) but I suppose this error is caused by the two others errors.

Can you help me please ?

Chances are your module usage flags have not been set exclusively to emitter.

I found this issue when spawning with the gap fix. Particles aren’t distributed evenly accross the trail, I suppose it’s related to delta time. How do you suggest to fix this?

If you want equal spacing, you can use a variation that snaps back the particle to it’s original spawn position once it is no longer the last one.

Hi, sorry for my late anwser but after trying to change my module usage I get several problems :Even by clearing the namespace I still get the error (screenshot show what it was before):

for the second as the node was not the same as yours I remove the part in the middle but no success in clearing the error either :

For the last one the flags are good so I don’t understand the error here :

I copy/paste the spawn rate module from epic game so I don’t know where I did something wrong.

Doesn’t look good to me, it’s set as a particle module in the usage flags.

You’ll want only Emitter spawn and Emitter Update

Oooooh I didn’t look there, I feel a bit silly now… I also had an interrogation : I have a missile effect using a ribbon as a trail with the event handler but there is a big gap, does the method you show is the same to fix it or is there some changes ?

Hi, so I’m following the tutorial (UE5.3) and made a copy of the SpawnRate module but it doesn’t compile with the following error message:
Cannot Set external constant, Type: int32 Variable: Engine.Emitter.TotalSpawnedParticles - Node: Map Set -

The only thing different that I can see is that the TotalSpawnedParticles variable has two namespace labels: Engine and Emitter. Any tips on what’s going on?

Well, I created a new Emitter variable instead of using the TotalSpawnedParticles and now everything works like a charm! :slight_smile: Cheers!

1 Like