Optimizing manipulation of thousands of parts

I am trying to create a custom particle system.

The current particle that I’m using consists of a part, surface gui and image label. This is currently the only way I’ve been able to replicate the default particle emitter particle visuals (emission and lightinfluence having been the biggest problems).

The problem is that for this custom particle system to be realistically usable I’d need it to be capable of spawning and manipulating thousands if not tens of thousands of these particle instances.

But currently at most I can spawn roughly 500 particles with next to no impact on performance. But when it reaches the 1k+ amount then:

  1. spawning 1k particles at once causes a small lag spike
  2. updating them every frame reduces overall framerate

The most notable optimizations currently are:

  1. I am using object pooling (reusing particles instead of creating new instances)
  2. particles are anchored and collisions, queries and touched are disabled

Currently the process of how I handle particles is:

  1. I get particle from object pool, put it inside an active particle array and update it’s values to the base values
  2. In RenderStepped I loop over each active particle and update it’s values correspondingly (this includes part CFrame and Size, ImageLabel color and transparency)
  3. When the particles lifetime expires i remove it from active particles and put it back inside object pool

I’ve also tried to use VectorForce instead of updating CFrames every frame but performance wise there were no notable changes. I also tried to use ImageHandleAdornments, which did have a notable increase in performance (or at least i believe they did) but for those I didn’t find a way to add emission to them, which makes them not suitable for particles.

I’ve checked the script profiler and from the looks of things the biggest problems currently are CFrame and Size changes.

Unfortunately I can’t really show the code itself since it’s rather large and split up.

tl;dr
how to optimize updating large quantities of parts

3 Likes

If you want to manage it optimally, consider using CollectionService. It’s designed for things like these.

Also, make sure all the part (assuming they won’t be visible) are CanCollide false, CanQuery false (ignored in raycasts) and CanTouch false. It will be easier on the engine.

Another question, why not just use a particle emitter? What custom behavior are you looking for?

1 Like

Try using BulkMoveTo to set all the particle CFrames at once instead of setting each one individually.

2 Likes

CollectionService from my understanding would be unnecessary since I already store all the of particles inside an easy to access array.

CanCollide, CanQuery and CanTouch are already disabled.

The reason i wish to do this is simply because the default particle emitters are rather limited for general vfx making. For example if i want to add turbulence (random velocity changes) then I can’t. Or if i want to add collision to some particles then I also can’t.

1 Like

This worked pretty great. Moving 1000 parts is now ~80ms faster (~44% faster). Though that is only if I disable Changed events for Position and Rotation, which in my case I can do with no issues. Otherwise if I do still trigger all changed events it’s roughly the same speed.

Would there happen to be any other similar bulk property change functions/methods that would work on other properies such as part size, imagelabel color and transparency?

Or since the majority of the lag seems to come from triggering event changes, would there happen to be any ways to change properies without triggering changed events?

I don’t think there are any other ones.

There is no way that I know of to set an instance property without firing the changed event (it doesn’t fire only for changes due to physics simulation and when properties change very frequently).

Have you tried using attachments instead of parts? For each particle, you could have an attachment with a particle emitter. By setting LockedToPart to true, the particles would have the same position as their attachments. The attachments could be parented to workspace.Terrain. Creating the particle would be as simple as particleEmitter:Emit(1). Changing the particle’s position could then be done by changing the attachment’s position.

Since you apparently aren’t using collisions and touched events, changing to this approach probably wouldn’t take anything important away from you. You could still use workspace: GetPartBoundsInRadius or raycasting if you need to find out whether parts are colliding with the particle.

I don’t know whether this is more efficient than your current approach, though.

1 Like

Why not just use multiple Particle Emitters that each give out 500 (or however u want) at once instead of trying to make One Particle Emitter give out 1000 particles.

Do you already make use of parrallel Luau? This can spread the workload (mostly calculations) to other threads.

You can use actors with bindable events and functions so you can basically invoke them to perform calculations and return the result.

Another thing (if you aren’t doing this already), you can also set a render distance for certain particles and just use Roblox default particles if it’s too far away from the camera.

If particle effects happen outside of the view and far away you most likely don’t need to do them at all unless the particles contribute to gameplay in some way.

And, this feature is currently still in beta but utilizing native Luau in module scripts should be able to make code closer to the speed of C++, sadly does not work in live games yet though.

1 Like

This is a really interesting idea and could potentially improve performance by a lot. Unfortunately this would require a major rewrite of my system and would limit it to being 2d.

If I am not able to get the performance to a desired level with my current approach then I’ll definitely consider this approach.

It doesn’t matter how many of my custom emitters I use. If I were to use 5 emitters that spawned 200 particles then it’d be the same as 1 emitter that spawns 1000 particles

The problem is that each particle is it’s own object and updating thousands of individual objects causes performance problems.

I’ll look into parrallel Luau but currently I don’t have complex logic so I doubt it’ll help much. Though it could still help a bit and I do eventually plan to make complex logic.

The majority of my performance problems seem to just come from changing thousands of properties every single frame.

Not rendering far away particles is something that’ll definitely help and I do plan on adding that.

I’ve also somewhat tried native but I haven’t noticed any improvements with it. It also not currently being available for live games is quite the drawback.

1 Like

Could you elaborate on what you mean by “limit it to being 2d”? If I understood correctly, you are already rendering the particles as 2D images embedded in 3D space since you are using an image label on a surface gui that is attached to a part.

If you were talking about movement, you can move an attachment anywhere in the 3D space so movement is not limited to two dimensions.

What I meant is that currently I’m trying to mimic the default particle emitter, where the particles are 2D (images), but later down the line I plan to also be able to use 3D objects (meshes) as particles. For example debris from an explosion would be 3D cubes. This is more or less the main reason I didn’t ever consider default particle emitters, since those are limited to displaying 2D images.

While yes, I could just simply script a debris module quickly, but that’d mean that half of the vfx would become scripted modules. My goal is to make something that would make it so I wouldn’t have to script modules for vfx but instead could just use one tool for everything vfx related.

1 Like