Collideable Particles FX [V4 cleanup]

A very performant Collisions Particles Module.

Wassup. Engines other than Roblox have Particles that can actually interact with the world. Roblox is really limited in what particles have to offer. So I decided to fix this issue.

I present to you...

Collideable Particles FX

But Why?

Here are some Examples.
Have you ever wanted to have a Realistic shower in roblox? Now you can:


Or maybe you wanted Cool sparks that collide with player:

Or Maybe you wanted Particles that can collide with each other:

Many other effects can be created with this, you just need to be creative!

Installation

Here is the module: Collision Particles FX
Here is file: ParticlesModuleRewrite.rbxm (16.8 KB)

Demo

Here is the place where you can test it out or edit it: PLACE

More Showcases



Tutorial

This Module is kinda hard to use due to the optimizations it benefits from.
So here is a tutorial on creating a emitter:
Step 1: Make a LocalScript that is inside Actor (It MUST be in a Actor).
Step 2: Edit the script. As first, you need to require() the Module.

local EmmiterCreator = require(CollideableParticlesModule)

Step 3: We make a New Emitter using:

local NewEmitter = EmmiterCreator.new()

Step 4: We change some Properties! So there are 3 Most Important Properties: EmmiterObject, Folder and Part. You must set: EmmiterObject - object that will spawn particles,
Folder - object that all particles will be parented to,
Part - object that will be spawned.
So we do something like this:

NewEmitter.Part = [Change This To Your Part]
NewEmitter.EmmiterObject = [Change This To Your Emitter]
NewEmitter.Folder = [Change This To Your Folder]

Step 5: Now you need to set other Properties. You can see what other properties do in other section of this Post.
Step 6: Now we just need to Enable our Particle Emitter. we do this using the Enabled property.
Finished Code should look something like this:

local EmmiterCreator = require(CollideableParticlesModule)
local NewEmitter = EmmiterCreator.new()

NewEmitter.Part = [Change This To Your Part]
NewEmitter.EmmiterObject = [Change This To Your Emitter]
NewEmitter.Folder = [Change This To Your Folder]

NewEmitter.Enabled = true

Congrats! You should now have particles spawning in game. If you still need help you can check the demo: (Particle Module - Roblox)
It is uncopylocked so you can edit it in Roblox Studio, and learn from it. If you still have issues, just reply under this post.

Properties

EmmiterObject • BasePart
Object that will Emit Particles.

Folder • Instance
Instance that particles will be Parented to when created.

Part • BasePart
Object that the emmiter will emmit.

Enabled • Boolean
Determines whenever Emitter is Emitting Particles.

Collisions • Boolean
Toggles collisions with the environment.

ColliderSize • Number
Radius of the collider.

LifeOnCollision • Number
Lowers the particles lifetime after collision. Setting to 0 makes them disapear on collision, setting it to higher than max lifetime does nothing.

CollisionChunkSize • Number
After update 4 collisions are chunked. Basically set it to a value between 8-16 depending on how far are the particles spread.

CollisionGroup • String
Uses Roblox Collisions Groups. Change it to group particles will use for collisions.

FixHumanoidCollisions • Boolean
After update to v4, parts with CanCollide turned off no longer collide with particles, by enabling this, you can revert better collisions with players. Still in testing.

Friction • Number
Simulates particles slowing down due to the traction of particle with the floor.

Bounce • Number
How strongly Particles Bounce. If 1, it bounces with the same velocity. If 0, it doesn’t bounce at all. If 0.5, it decreases velocity by half when bouncing.

Speed • NumberRange
Determines starting speed of Particles when they are created.

InheritSpeed • Number
Determines how much Emitter Velocity affects Particles Velocity.

Spread • Vector2
The spread of Particles. If 0 they will all go in one direction.

Rate • Number
How many Particles are created per second.

LifeTime • NumberRange
How long Particles will last.

Drag • Number
How much air slows down the particles.

Acceleration • Vector3
Force applied on Particles. Can be used to create Gravity.

SelfCollisions • Boolean
Determines if Particles can collide with each other instead of only with the world.

SelfCollType • SelfCollType (Custom Enum)
It changes the collisions handling between particles when SelfCollisions is turned on. Enums are accessible in Module.

Iterations • Integer
Determines how many times Collisions Between Particles are calculated. The higher the number, the less glitchier it is. (It should be keept at 1 unless using Realistic collisions).

SelfSize • Number
Determines Size (Radius) of Collisions Between Particles.

Mass • Number
It determines the mass of the particles for Collisions Between Particles. Only works for ‘Realistic’ Collision Type. Doesn’t change collisions with the world.

FluidForce • Number
Determines the force of fluid Particles. Only works for ‘Fluid’ Collision Type.

SimulateTurbulence • Boolean
Property that fakes Wind Turbulence by objects. Not physically correct and using a hacky way. Needs a revamp.

CachceFolder • Instance [DEPRECATED in Version 2.1]
If you want to prevent high memory usage for better FPS you can specify a Folder where parts will be Parented to instead of being deleted. Must Be Different than Folder. Must be original to this Particle Emitter.

CollisionComplexity • Boolean [DEPRECATED]
When it is on, Collisions will use Sphere, when it’s off, Collisions will use Box. Low Performance Impact.

MinimumCachce • Number
Defines how much particles are kept in memory without being destroyed. Useful for burst particles rather than constant rate particles emmision (like explosions).

FaceCamera • Boolean
Toggles whenever particles face the camera. If false, particles face the direction they are moving.

CameraCulling • Boolean
Toggles whenever particles position update when they are not visible to camera. Should be mostly keep on unless facing issues. If particles use trails, use the TrailFix Property instead of turning this off.

CullingRadius • Number
Defaults to ColliderSize if nil. Changes how big the sphere is that is used for CameraCulling. It is recommended to set it to the size of the Part that is being emitted in case ColliderSize property is smaller than part.

RenderDistance • Number
Ingored when CameraCulling is Disabled. Changes how far particles can render. Particles are still simulated past the RenderDistance, so they should be disabled by code.

Wind • Boolean
If true, Particles will be affected by wind.

WindSpeed • Number
How fast wind flows.

WindScale • Number
How frequent are the changes in the perlin noise of wind.

WindStrenght • Number [DEPRECATED in Version 4]
How strong wind is.

WindDifference • NumberRange
Controls the min and max force by wind. Setting it to (-1, 1) makes the particles sway in both ways.

WindDirection • Vector3
Direction of wind.

DistanceFPS • Number
Throttles Particles that are far away. So if you set it to 24, then the distance from camera is 24, then it will update by half FPS, and if distance was 72, it would update by quarter FPS

EveryFrame • Function
This is a function that can be used to change particle Part properties every frame. Here is an example on how you can use it:

--changes EveryFrame property of Emitter to function that will change particle size based on Velocity
function Emitter:EveryFrame(Pos : Vector3, Velocity : Vector3, LifeTime : number)
	return {Size = Vector3.new(0.2, 0.2, Velocity.Magnitude / 50)}
end

--You can also change other properties of Particle. For example this code changes Color based on Velocity
function Emitter:EveryFrame(Pos : Vector3, Vel : Vector3, LifeTime : number)
	return {Color = Color3.new(0, 0, 1):Lerp(Color3.new(1, 0, 0), math.clamp(Vel.Magnitude / 40, 0, 1)}
end

--You can also change multiple values. This code changes Size and Color based on Velocity.
function Emitter:EveryFrame(Pos : Vector3, Vel : Vector3, LifeTime : number)
	return {Size = Vector3.new(0.2, 0.2, Velocity.Magnitude / 50), Color = Color3.new(0, 0, 1):Lerp(Color3.new(1, 0, 0), math.clamp(Vel.Magnitude / 40, 0, 1)}
end

--You can also change properties of desceants of the particle. This code changes particle's Trail Color based on Velocity.
function Emitter:EveryFrame(Pos : Vector3, Vel : Vector3, LifeTime : number)
	return {["MyColorTrail"] =  {Color = Color3.new(0, 0, 1):Lerp(Color3.new(1, 0, 0), math.clamp(Vel.Magnitude / 40, 0, 1)}}
end

OnCollision • Function
This function can be used to change Particles Properties. It works like EveryFrame function but happens only on collision so it has better performance. It also returns the Collided part as the 4rd argument.

OnSpawn • Function
This function can be used to change Particles Properties. It works like EveryFrame function but happens only when particle is created. Useful for randomizing the Particles, or returning them to the original state.

BeforePhysics • Function
This function runs before Physics Calculations. Can be used to AddForces.

TrailFix • Table
Fixes the trails when using CameraCulling or when particles despawn. Here is a example usage:

--[[
First one is setting it as a string which will be the trail’s name. The trail must be directly a child of part you emit. So if your trail’s name is for example “Trail”, then you need to do:
]]
Emitter.TrailFix = "Trail"

--[[
Second one is, when you have multiple trails that are children of the emitter Part. So if the Trail’s names are for example “TrailA”, “TrailB” and “TrailC” then you need to do:
]]
Emitter.TrailFix = {"TrailA", "TrailB", "TrailC"}

--[[
Third one is, when your Trail or multiple Trails aren’t a Children of the Part but a Desceants, then you need to make a table, and inside that table, make tables that are the paths to the trails. So let’s say that the emitted objects has a child for example named “TrailsFolder”, and TrailsFolder has Trails like “TrailA” and “TrailB” then you need to do:
]]
Emitter.TrailFix = { {"TrailsFolder", "TrailA"}, {"TrailsFolder", "TrailB"} }
Functions

AddForce(Force : ForceTypeEnum)
It is used to add Forces on Particles. Forces don’t stay forever, you need to add them every frame.
There are currently 3 ForceTypes you can use:

  • Repel: Repels all Particles in Range. (Can be used to make Explosions or Planet Gravity and more)
  • RepelConstant: Repels all particles in Range but Force isn’t affected by Distance.
  • Turbulence: Adds Turbulence Force. (Can be used in pair with WindPhysics to make it look more realistic)
    Here is a tutorial how to use it:
--First we make a Emitter
local EmmiterModule = require([Replace This With Place Where You Put Module].ParticleEmmiter)
local YourEmitter = EmmiterCreator.new()

--Next we change some properties (in this case i only change the important ones)
YourEmitter.Part = [Change This To Your Part]
YourEmitter.EmmiterObject = [Change This To Your Emitter]
YourEmitter.Folder = [Change This To Your Folder]

-- We change BeforePhysics Function. We will use it to add Force Every Frame
function YourEmitter:BeforePhysics()
	--We get the Player's Character
	local Char = game.Players.LocalPlayer.Character
	--Then we check if it exists
	if Char then
		--We get Player's Root
		local HRP : Part = Char:FindFirstChild("HumanoidRootPart")
		--And we also check if it exists
		if HRP then
			--Now we make a new Force that Attracts Particles to player
			local MyForce = EmmiterCreator:NewForce(EmmiterCreator.TypeForce.Repel)
			--Notice how we change the power of the Force to a negative number. This makes it so that particles are Attracted, not Repeled
			MyForce.Power = -3
			MyForce.Range = 10
			MyForce.Position = HRP.Position
			Emitter:AddForce(MyForce)
		end
	end
end

YourEmitter.Enabled = true

SyncToRenderToggle(Boolean)
By running this function with Boolean set to True, you can make the particles simulate before Render instead of after every Physics Step. Might introduce stuttering, but might make it look smoother when player is using above 60 FPS.

Emit(Amount)
It emits an Amount of Particles. Can be used to create explosions.

Destroy()
Makes sure it’s all cleanup when don’t need to use the Emitter anymore, making sure there are no memory leaks.

UpdateLog

Release
The Release of this module

Version 1.1
-Added Better Particles Spread (Now it works like default Roblox ParticleEmitter Spread).
-Added new Property DistanceFPS. Check Properties Section for info.

Version 1.2
-Changed Friction and Drag Code to one that better adapts to FPS.

Version 2
-2x better FPS! It was possible by merging 2 modules into 1 and using BulkMoveTo().

Version 2.1
-Added new and faster Cachce Code. CachceFolder is now Deprecated.

Version 2.2
-Added new property called EveryFrame. It can be used to change Particles properties every frame. You can find more info about it in Properties Section.

Version 2.3
-Added new property called CameraCulling.

Version 2.4
-Added new property called InheritSpeed.
-Improved Velocity calculation with moving objects. Now Spinning Parts Affect Velocity Too.

Version 2.5
-Changed Property type of Speed and LifeTime from Number to NumberRange. It will be easier now to spawn particles with randomized Speed and LifeTime.
-Added new Property called MinimumCachce. It makes it so that number of cachced particles cannot be smaller than this value. Useful for creating Explosions without lag.

Version 3
-Changed Collision Code to be more accurate. Now particles don’t get stuck in players.
-Added new Function AddForce. It can be used to add Forces like Repel or Turbulence.
-Added new Property called BeforePhysics.

Version 3.1
-New Three Experimental Properties: SelfCollisions, SelfSize, Iterations. It makes it possible to simulate particles that collide with eachother.
-New Property SimulateTurbulence. It tries to fake Objects affecting wind which affects Particles.

Version 3.2
-New Three Properties: FluidForce, Mass, SelfCollType. They are used to better customize Collisions between Particles.
-Better Collisions Code.

Version 4
-Collisions are now chunked. Makes Emitters that have particles close to eachother reuse the Collision Objects.
-Better Frustum Culling. More particles are culled now.
-Added TrailsFix property. Fixes trails on particles.
-Some of the Wind properties now have different behaviour than in the previous versions.
-You can now make particles simulate before render instead of after every physics step.
-Better distance throttling. Makes it slightly less visible.
-Particles can now disappear when colliding with objects, or disappear faster after collision. Use the LifeOnCollision property.
-Particles no longer collide with NonCanCollide objects.
-You can now assign the CollisionGroup that particles will use.
-Module now uses the new vector library for better performance compared to Vector3.
-You can now disable Collisions for particles.
-OnCollision now returns the collided object as the 4rd argument.

Custom Physics Engine

It doesn’t rely on Roblox physics, instead the particles are simulated using custom code. It comes both with advantages and disadvantages. Advantage is that it performs faster than Roblox physics, but disadvantage is it doesn’t work on MeshParts (yet!).

Why should you use this?

It makes the particles feel like they are actually a part of the world. It also makes the game feel more alive.

Inspiration

I was inspired by the Nvidia PhysX/Flex technology. Seeing thousands of particles collide was ultra satisfying to me. I thought i would try to do something similar. The main point of this Module is to make collideable particles with the lowest performance impact making it usable in production games.

Remember to not replace every roblox default particle emitter with this. While in fact this module is pretty fast, still using it too much might lag your game. Use it moderatelly or let the players customize the particles rate by giving them settings menu.

That’s all! If you have any ideas or questions, you can reply to this topic.

The module doesn’t use Raycast at all. It uses it’s own collisions code for the best stability and performance.

How Would You Rate this Module?

  • 5 Stars
  • 4 Stars
  • 3 Stars
  • 2 Stars
  • 1 Stars

0 voters

Also, if you need help implementing it to your game, just leave a reply, so i can help you.

206 Likes

I was Able to Make Snow Using this module:

27 Likes

Very cool! It would be neat if particles that are below a certain speed or are far away from the camera update less often to save performance

11 Likes

Looks cool! I think the test place is set to private.

1 Like

Roblox VFX have now advanced thanks to this module!

3 Likes

This looks amazing! Great job!

3 Likes

Oh, im stoopid. Now i fixed it. You should be able to test it now.

This is a really good idea. I will try to implement this feature in next update.

Looks awesome! I’ll definitely try something out with this, really gives great vfx ideas

Update
-Changed Spread Property to act like Roblox Default ParticleEmmiter Property.
-Added New Property Based on @baseparts’s idea called DistanceFPS. It makes so that Particles over this distance will update with lower FPS. The further the camera is, the Lower the FPS.

5 Likes

Update
-Changed Friction and Drag Code to work with every FPS

With DistanceFPS Optimization i was able to simulate 3780 Particles :exploding_head: :scream:

12 Likes

Update 2
Ok, this update can blow you mind. :exploding_head:
Before the update, when simulating 6300 Particles and setting DistanceFPS to 20 i had somewhere around ~30 FPS.
After this update, when simulating the same amount of Particles with the same DistanceFPS i was getting 60FPS! :exploding_head: :exploding_head: :exploding_head:
Of course, if i would set DistanceFPS to something higher, i would not have 60 FPS, but still this is impressive.
To get this amount of FPS i merged 2 Modules into 1 and used something called BulkMoveto()

I cannot wait until someone will post their creation with this module!

18 Likes

That’s a huge improvement, I’m definitely going to use this inside of my game. Congrats on an amazing module!

Here's some more stuff I think could save performance
  • This won’t save performance by much but I notice you make new params every heartbeat. Since your FilterDescendantsInstances are folders you could make the params outside of heartbeat and reuse them.

  • I notice you reparent dead particles to the Cache folder, I suggest just leaving all particles inside the same folder since you’re already moving dead particles to a far away cframe. Instead you could insert your dead particles inside a cache table which can be use later.

9 Likes

Can you provide an RBXM file instead of the module link or alongside it? it’s way more convenient

4 Likes

First of all. The average rbxm file size is 800 kb (i’m being generous), You would need 1,497 rbxm files to fill 1 GB and 179,657 rbxm files to fill 120 GB

Second of all. It’s not a bad idea and would be even very useful if you are organized or use external editors. For example, Let’s say you are developing your game and you need a particle collision system for your game ystem so you decide to install this, But oh noo it’s a roblox model so now you have to install it, close your game, reopen it, insert the model, sync it with your editor to be able to use it

Third of all. It’s really a personal preference

1 Like

Ok, i will do that when i will have free time.

Update 2.1
-Replaced Cachce Code with a better one. Now you should see a 8 FPS difference.
-CachceFolder Property is now Deprecated

Now I can make better particles (don’t worry I won’t replace it with the default particle emitter from roblox)

1 Like

!!!WARNING!!!
In Version 2.1 there was a bug in code that could Massivelly destroy your FPS. If you downloaded Version 2.1 before this message, please reinstall it!