Projectile lag?

Hello, I’m working on a spell system for myself, with no end. I’ve been working on the module for some time, and I still have a problem that I couldn’t fix.

There are several spells and functions customized to work correctly, and everything seems to work normally, but you may notice some flaws right after using it for some time. For each spell, there is a specific type of cooldown, and some players selected by me have no cooldown, so they have the ability to spam/cast the spells as many times as they want.

I use a RemoteEvent to send information to the server like mouse position and other features. I even use some checkers to check if they have the spell before executing the spell function.

If you have a cooldown on spells, you can use spells normally without any problem, but when those without cooldown use a spell, for example, 100 times per second, there are many effects and other characteristics created, and the movement of these spells ends up getting very slow. I have seen several games with the same features, but none of this happens. I would like to know what I possibly am doing wrong to remove this problem and make my spells move normally regardless of the number of spells being casted at once.

Check my SpellBase function which creates the effects between spells:

function Events.SpellBase(MousePos, Tool, Sound2, SpellColors, F, Name, Self, IsSpellbolt)
	local Character = Tool.Parent
	local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
	local WandTip = Tool:WaitForChild("WandTip") -- Where the spell beam will come from
	
	local Player = Players:GetPlayerFromCharacter(Character)
	local Head = Character:WaitForChild("Head")
	local NametagColor = Head:FindFirstChild("Nametag"):FindFirstChild("Frame"):FindFirstChild("Name").TextColor3 or Color3.new(1, 1, 1) -- May be used for spellbolts color
	
	local IsRainbow = false -- Custom rainbow color for spells
	
	if Player ~= nil then
		if Player.UserId == 646638413 then
			IsRainbow = true
		elseif Player.UserId == 105137002 then
			IsRainbow = true
		end
	end
	
	local SpellPart = Instance.new("Part")
	local SpellDuration = tick() -- Current time upon being casted
	local SpellTime = 10 -- Max time before being destroyed
	
	if IsSpellbolt == false or not IsSpellbolt then -- THIS WILL APPLY ALL THE FFECTS
		if Name == "Aguamenti" or Name == "Incendio" or Name == "Glacius" then
			local SpellParticle = ParticleSpellPart.ParticleTrail:Clone()
			SpellParticle.Color = ColorSequence.new(SpellColors[1])
			SpellParticle.Parent = SpellPart
		elseif Name == "Periculum" or Name == "Morsmordre" then
			local Left1 = BasicSpellPart.Left1:Clone()
			local Left2 = BasicSpellPart.Left2:Clone()
			local MovementScript = BasicSpellPart.Movement:Clone()
			local Right1 = BasicSpellPart.Right1:Clone()
			local Right2 = BasicSpellPart.Right2:Clone()
			local TrailThin = BasicSpellPart.TrailThin:Clone()
			local TrailWide = BasicSpellPart.TrailWide:Clone()
			
			Left1.Parent = SpellPart
			Left2.Parent = SpellPart
			MovementScript.Disabled = false
			MovementScript.Parent = SpellPart
			Right1.Parent = SpellPart
			Right2.Parent = SpellPart
			TrailWide.Color = ColorSequence.new(SpellColors[1])
			TrailThin.Color = ColorSequence.new(SpellColors[2])
			TrailWide.Attachment0 = Left1
			TrailWide.Attachment1 = Right1
			TrailWide.Parent = SpellPart
			TrailThin.Attachment0 = Left2
			TrailThin.Attachment1 = Right2
			TrailThin.Parent = SpellPart
		elseif Name == "Incarcerous" then
			local Left1 = BasicSpellPart.Left1:Clone()
			local MovementScript = BasicSpellPart.Movement:Clone()
			local Right1 = BasicSpellPart.Right1:Clone()
			local TrailThin = BasicSpellPart.TrailThin:Clone()
			
			Left1.Parent = SpellPart
			MovementScript.Disabled = false
			MovementScript.Parent = SpellPart
			Right1.Parent = SpellPart
			TrailThin.Color = ColorSequence.new(SpellColors[1])
			TrailThin.Attachment0 = Left1
			TrailThin.Attachment1 = Right1
			TrailThin.Parent = SpellPart
		else
			local Left1 = BasicSpellPart.Left1:Clone()
			local Left2 = BasicSpellPart.Left2:Clone()
			local MovementScript = BasicSpellPart.Movement:Clone()
			local Right1 = BasicSpellPart.Right1:Clone()
			local Right2 = BasicSpellPart.Right2:Clone()
			local TrailThin = BasicSpellPart.TrailThin:Clone()
			local TrailWide = BasicSpellPart.TrailWide:Clone()
			
			Left1.Parent = SpellPart
			Left2.Parent = SpellPart
			MovementScript.Disabled = false
			MovementScript.Parent = SpellPart
			Right1.Parent = SpellPart
			Right2.Parent = SpellPart
			TrailWide.Color = ColorSequence.new(SpellColors[1])
			TrailThin.Color = ColorSequence.new(SpellColors[2])
			TrailWide.Attachment0 = Left1
			TrailWide.Attachment1 = Right1
			TrailWide.Parent = SpellPart
			TrailThin.Attachment0 = Left2
			TrailThin.Attachment1 = Right2
			TrailThin.Parent = SpellPart
		end
	elseif IsSpellbolt == true then
		local Left1 = BoltSpellPart.Left1:Clone()
		local Left2 = BoltSpellPart.Left2:Clone()
		local ParticleTrail = BoltSpellPart.ParticleTrail:Clone()
		local Right1 = BoltSpellPart.Right1:Clone()
		local Right2 = BoltSpellPart.Right2:Clone()
		local TrailThin = BoltSpellPart.TrailThin:Clone()
		local TrailWide = BoltSpellPart.TrailWide:Clone()
		
		local BoltColor = Color3.new(NametagColor.r, NametagColor.g, NametagColor.b)
		
		if IsRainbow == true then
			BoltColor = Color3.new(math.random(), math.random(), math.random())
		end
		
		local ColorTable = {
			BoltColor.r,
			BoltColor.g,
			BoltColor.b
		}
		
		if ColorTable[1] <= 0.1 and ColorTable[2] <= 0.1 and ColorTable[3] <= 0.1 then
			ColorTable[1] = 0.1
			ColorTable[2] = 0.1
			ColorTable[3] = 0.1
		end
		
		Left1.Parent = SpellPart
		Left2.Parent = SpellPart
		ParticleTrail.Color = ColorSequence.new(Color3.new(ColorTable[1], ColorTable[2], ColorTable[3]))
		ParticleTrail.Parent = SpellPart
		Right1.Parent = SpellPart
		Right2.Parent = SpellPart
		TrailWide.Color = ColorSequence.new(Color3.new(ColorTable[1], ColorTable[2], ColorTable[3]))
		TrailThin.Color = ColorSequence.new(Color3.new(ColorTable[1], ColorTable[2], ColorTable[3]))
		TrailWide.Attachment0 = Left1
		TrailWide.Attachment1 = Right1
		TrailWide.Parent = SpellPart
		TrailThin.Attachment0 = Left2
		TrailThin.Attachment1 = Right2
		TrailThin.Parent = SpellPart
	end
	
	SpellPart.Massless = false
	IgnoredItems.AddToIgnoreList(SpellPart) -- Ignore list for raycasting
	SpellPart.Parent = Workspace
	
	if Self == true then -- Checks if they're using a spell on themselves
		SpellPart.CFrame = WandTip.CFrame:ToWorldSpace(CFrame.new(-1.5, 0, 0))
	elseif Self == false or not Self then
		SpellPart.CFrame = WandTip.CFrame
	end
	
	SpellPart.CFrame = CFrame.new(SpellPart.CFrame.p, MousePos) -- Set its CFrame
	SpellPart.Name = "Spell"

	local Angle = (MousePos - SpellPart.CFrame.p).Unit
	
	SpellPart.Size = Vector3.new(1, 1, 1)
	SpellPart.Material = Enum.Material.Plastic
	SpellPart.Transparency = 1
	SpellPart.CanCollide = false
	SpellPart.Anchored = true
	
	local Creator = Instance.new("ObjectValue", SpellPart)
	Creator.Value = Player
	Creator.Name = "Creator"
	
	local IsSpell = Instance.new("BoolValue", SpellPart)
	IsSpell.Name = "IsSpell"
	IsSpell.Value = true
	
	local Collided = Instance.new("BoolValue", SpellPart)
	Collided.Value = false
	Collided.Name = "Collided"
	
	local SpellName = Instance.new("StringValue", SpellPart)
	SpellName.Value = Name
	SpellName.Name = "SpellName"
	
	if IsSpellbolt == true then -- Checker
		Self = nil
	end
	
	SpellPart.Color = SpellColors[1]
	
	local WandPower = (5 * 1.35) / 1.15
	if Player ~= nil then
		WandPower = (5 * _G.PlayerData[Player.UserId]["WandStatsData"]["Power"]) / 1.45
	end -- WandPower calculates their spell speed (I already tested and it's fine)
	
	local Connection -- Movement Function
	Connection = RunService.Stepped:Connect(function()
		if not SpellPart then
			Connection:Disconnect()
		end
		local SpellRay = Ray.new(SpellPart.Position, SpellPart.CFrame.LookVector * 5)
		local Hit,Position = Workspace:FindPartOnRayWithIgnoreList(SpellRay, IgnoredItems.GetIgnoredParts())
		EXPLOSION_POSITIONS[SpellPart] = Position -- Upon being hit, the collision effect will be placed at this position (works v well)
		if SpellPart.Name == "BouncedSpell" then
			SpellPart.CFrame = SpellPart.CFrame - SpellPart.CFrame.LookVector * 5
		else
			SpellPart.CFrame = SpellPart.CFrame + SpellPart.CFrame.LookVector * WandPower
		end
		if math.ceil(tick() - SpellDuration) >= SpellTime then -- Checks if the spell can be destroyed already
			for _, v in pairs(SpellPart:GetDescendants()) do
				if v:IsA("Trail") then
					v.Enabled = false
				elseif v:IsA("ParticleEmitter") then
					v.Enabled = false
				end
			end
			Events.AddToDebrisService(SpellPart, 2)
			Connection:Disconnect()
		end
		if Hit then
			if Self == false or not Self then
				if Hit:IsDescendantOf(Character) then
					return
				end
			end
			if Events.FindShieldReflector(Hit) then
				SpellPart.Name = "BouncedSpell"
				return
			end
			if IsSpellbolt == true then
				Events.SpellboltDetection(SpellPart, Tool, Position, Hit, IsSpellbolt, SpellName, F, Sound2, false)
			elseif Self == false or not Self then
				Events.CollisionDetection(SpellPart, Tool, Position, Hit, IsSpellbolt, F, Sound2, false, SpellName.Value)
			elseif Self == true then
				Events.CollisionDetection(SpellPart, Tool, Position, Hit, IsSpellbolt, F, Sound2, true, SpellName.Value)
			end -- These are the collision functions (works v well also)
			Connection:Disconnect()
		end
	end)
	
	SpellPart.Changed:Connect(function(Property)
		if Property == "Parent" then
			IgnoredItems.RemoveIgnoredPart(SpellPart)
			IgnoredItems.RemoveIgnoredPart(Character)
			if Connection ~= nil then
				Connection:Disconnect()
			end
		end
	end)
	
	return SpellPart -- Returns the spell to the function that called it (won't change anything tho)
end
2 Likes