logo80lv
Articlesclick_arrow
Talentsclick_arrow
Events
Workshops
Aboutclick_arrow
profile_login
Log in
0
Save
Copy Link
Share

How to Create Dynamic Tentacle and Rope Simulation Inspired by Returnal

Adiyar Aidarbekov told us about his GPU tentacle and rope simulation inspired by Returnal, showing the Niagara workflow in Unreal Engine and explaining collision setup.

Dynamic Tentacles and Ropes

Inspired by Housemarque’s GDC presentation on Returnal – where it leveraged a custom GPU-driven chain system for projectiles, tentacles, and foliage – I wanted to see if a similar setup could be built completely within Unreal Engine's Niagara framework without relying on a custom C++ architecture. 

The Niagara system and its Scratch Pad are no longer just tools for explosions and magic – they’re a fully customisable HLSL compute environment with tons of presets and examples from Epic Games' developers themselves, which you can easily open and see what’s happening inside each module from ColorScaler to GPU Collision logic and RayTracing.

Initial Setup

When I started it, I had no idea what HLSL was and where to begin. Since there were not many tutorials or even documentation about Niagara Scratch Pads. I had to follow Unreal Engine advanced examples and reverse-engineer them to learn.

Niagara has an Attribute Reader that allows emitters and particles to extract data from other particles and assign variables to themselves based on the output.

So, to make a rope constraint simulation, I needed to implement a child-to-parent constraint logic. This creates the hierarchy across the array of particles in chain order.

By managing Niagara's native attributes like UniqueID, RibbonLinkOrder, and RibbonID, you can index and evaluate particles one by one, allowing an Attribute Reader to map data across the entire chain.

Finally, I managed to make it work as a one-sided constraint behaviour: if we have sorted the order and read the parent position using the Attribute Reader, we can change the particles' position and snap them to the parent.

The HLSL checks if the child particle has moved too far from the parent particle (defined by maxDistance), and if it has, snaps the child particle back to the parent's clamped position.

Environment Distance Field Collisions

The default collision module offers a lot already, but it has “safety” options to kill or cull particles if they penetrate the surface too deeply. So I needed a simple but solid “Do Not Clip or Die” logic. Especially if one particle dies along the rope, it breaks the order of neighbors and the constraint logic itself.

To bypass this, I wrote a custom simplified collision script using the same Signed Distance Field (SDF) as the original collision module.

The script accesses CollisionQuery values, and from there, particles can read position and distance to the Distance Field and snap the particle back to the surface if it penetrates it.

And here we have it:

The order of modules is crucial:

  • Velocity should be calculated before the constraint stage because of PBD mechanics.
  • SDF_OffsetCorrection goes after constraints because it's a prioritized module, which overrides previous values if a collision happens.
  • CalculateAccurateVelocity should be at the very end, after all position modifications.

Eventually, the whole emitter looks like this:

Two-Sided Constraint and PBD

One-sided constraint worked well but wasn’t realistic enough to behave like an actual rope. 

To implement a stable, two-sided constraint that behaves naturally and can be pulled/pinned from both ends, I made a custom Position-Based Dynamics (PBD) solver using Verlet integration.

Here’s an informative article about Verlet rope logic and PBD:

The code and emitter are a bit long to explain in this article, but I’ll show the main points:

It basically works the same way as a one-sided constraint, but in this case, it reads child parameters as well:

And instead of snapping the particle back to the parent constraint, it snaps to the middle value between parent correction and child correction:

On top of that, there’s an ends constraint module that basically locks zero and last ribbon particles in a specified place by using normalized RibbonLinkOrder (defines if it’s a zero/last particle in the chain):

Additionally, there is one hard rule: don't calculate velocity in PBD. It gets solved after all position transforms by CalculateAccurateVelocity, using the position delta – which is the base of the PBD solver – taking the previous position and current position and calculating velocity from their delta.

I ended up making not just a VFX pack but a tool that can be used in many different ways: to make not only tentacles but also vegetation, vines, grass, ropes, chains, and more.

Here are the results and possible uses:

Asset Packs

I packaged this entire research into two separate toolsets available on FAB under FXology. Both packs will be part of the Fab Summer Mega Sale (July 21-29); add them to your wishlist now to catch the discount:

If you have any questions, you can find me on FAB, X, YouTube, Discord, and LinkedIn.

Adiyar Aidarbekov, Senior VFX Artist

Built for Creators. Read by the Best
Partner with 80 Level

Comments

0

arrow
Type your comment here
Leave Comment
Built for Creators. Read by the Best
Partner with 80 Level

We need your consent

We use cookies on this website to make your browsing experience better. By using the site you agree to our use of cookies.Learn more