How to manage/work with particles?

Heyo I’ve been trying to add some particles to my game and something I’ve found annoying is I just can’t find a way to manage them and work with them nicely I’m currently using like 3 scripts to do it.

Currently I have 2 particle emitters for running and then one for jumping which I add to the players humanoidrootpart when they join so that’s script 1. I then have a module script which uses Humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function() and Humanoid:GetPropertyChangedSignal("Jump"):Connect(function() to detect when the player is walking or jumping which then fires a remote to the server which then runs a function in another module script so yeah it’s like 4 scripts.

So my question is what’s a nicer way to do all of this?

--Script 1
Players.PlayerAdded:Connect(function(Player)
	Player.CharacterAdded:Connect(function(Character)
		local Humanoid = Character:WaitForChild("Humanoid")
		local HRP = Character:WaitForChild("HumanoidRootPart")
		local SmokeLeft = FX["Particles"].LeftRun:Clone()
		local SmokeRight = FX["Particles"].RightRun:Clone()
		SmokeLeft.Parent = HRP
		SmokeRight.Parent = HRP
	end)
end)
--Script 2
Humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
	if HRP:FindFirstChild("LeftRun") and HRP:FindFirstChild("RightRun") and Humanoid.FloorMaterial ~= Enum.Material.Air then
		if Humanoid.MoveDirection == Vector3.new(0,0,0) then
			--print("STOPPED")
			Test:FireServer{Particles = {HRP:FindFirstChild("LeftRun").RunParticles,HRP:FindFirstChild("RightRun").RunParticles},Type = "Run",Fade = "Out"}
		else
			--print("WALKING")
			Test:FireServer({Particles = {HRP:FindFirstChild("LeftRun").RunParticles,HRP:FindFirstChild("RightRun").RunParticles},Type = "Run",Fade = "In"})
		end		
	end
end)

--For Jumping
Humanoid:GetPropertyChangedSignal("Jump"):Connect(function()
	if Humanoid.Jump and (Humanoid.FloorMaterial ~= Enum.Material.Air) then
		Test:FireServer({Type = "Jump"})		
	end
end)
--Script 3
Test.OnServerEvent:Connect(function(Player,Table)
	if Table["Type"] == "Jump" then
		FX.Jump(Player.Character,Table["Particle"])
	else
		FX.Run(Table["Particles"],Table["Fade"])
	end
end)
--Script 4
local Particles = {}

function Particles.Toggle(Player,Bool)
	for _,Plr in pairs(game:GetService("Players"):GetPlayers()) do
		if Plr.Name ~= Player.Name then
			for _,Particle in pairs(Plr.Character:GetDescendants()) do
				if Particle:IsA("ParticleEmitter") then
					Particle.Enabled =  Bool					
				end
			end
		end
	end
end

function Particles.Run(Particles,Fade)
	if Fade == "Out" then
		for _,Particle in pairs(Particles) do
			for Rate = 6,0,-1 do
				Particle.Rate = Rate
			end
		end		
	elseif Fade == "In" then
		for _,Particle in pairs(Particles) do
			for Rate = 0,6,1 do
				Particle.Rate = Rate
			end
		end				
	end
end

function Particles.Jump(Character)
	--Fade out the run particles
	if Character.HumanoidRootPart:FindFirstChild("Left") and Character.HumanoidRootPart:FindFirstChild("Right") then
		Particles.Run({Character.HumanoidRootPart:FindFirstChild("LeftRun").RunParticles,Character.HumanoidRootPart:FindFirstChild("RightRun").RunParticles},"Out")		
	end
	
	local Origin = Character.HumanoidRootPart.Position
	local Direction = Vector3.new(0,-500,0)
	local Params = RaycastParams.new()
	Params.FilterType = Enum.RaycastFilterType.Blacklist
	-- blacklist is essentially the same thing as ignore list
	Params.FilterDescendantsInstances = {Character:GetChildren()}
	local Result = workspace:Raycast(Origin,Direction,Params)

	if Result then
		local HitPart = Result.Instance
		if not Character.HumanoidRootPart:FindFirstChild("JumpFX") then
			local Part = Instance.new("Part")
			Part.Name = "JumpFX"
			Part.Shape = "Cylinder"
			Part.Transparency = 1
			Part.Color = Color3.fromRGB(0, 255, 0)
			Part.Size = Vector3.new(0.05, 3.44, 3.388)
			Part.Position = Vector3.new(Result.Position.X,Result.Position.Y,Result.Position.Z)
			Part.Orientation = Vector3.new(0,0,90)
			Part.Anchored = true
			Part.CanCollide = false
			Part.Parent = Character.HumanoidRootPart

			local JumpParticle = FX.Particles.JumpParticles:Clone()
			JumpParticle.Parent = Part
			JumpParticle:Emit(10)
			game.Debris:AddItem(Part,3)			
		end
	end
end

return Particles
2 Likes

I’m not sure but don’t humanoid properties like move direction and events replicate automatically? If so you can just do this entirely on client with character added in starter player scripts like what @iGottic did by measuring the velocities on the client and then doing IK towards that velocity.

I’m on mobile and can’t check rn sorry.

1 Like

heres a great toturial

1 Like

I don’t need help making particles. I’m trying to ask like how can I manage or work with them in code and have some kind of system to enable/disable particles or adjust properties etc.

2 Likes

You can change properties just like anything else. You can change a particle’s velocity just as easily as you can change a part’s color, for example.

I recommend you take a look at the API page, it’s incredibly helpful! ^-^

1 Like

I guess my question was a bit confusing so my bad, but yeah so I know how to change the properties, but i feel like the way I currently handle my particles are clunky and could be done a lot better than using 4 scripts.

You should never have more than 2-5 scripts in your game total.

I would recommend putting one server script, and one client script. From there, neaten your code by using TweenService instead of manual for-loop interpolation, proper parenthesis syntax, etc. Keeping your code organized is key to spotting issues.

1 Like