How can I optimise my lightning strikes?

Hello! I’ve recently created a subclass for my snowball weapon which produces lightning strikes upon impact. However, I’ve noticed every time the method responsible for it is called, the fps goes down by 2 or 3, which concerns me because it adds up to alot if everyone is continuously calling it.

Here is a video of it in action:
https://gyazo.com/a95615c12675ac2c1db0e57fe1c6d147

Subclass I wrote:

local SnowballSuper = require(script.Parent.Parent.SnowballSuper)
local Sub = setmetatable({}, SnowballSuper)
Sub.__index = Sub

function Sub.new(origin, direction, speed, gravity, damage, cosmeticSnowball, ignoreList, extraAssets)
	local self = setmetatable(SnowballSuper.new(origin, direction, speed, gravity, damage, cosmeticSnowball, ignoreList), Sub)
	self.extraAssets = extraAssets
	
	return self
end

function Sub:PlayImpactEffect(position)
	print("lighting effect")
	local MAX_DISTANCE = 50
	local BOLT_SEMGENTS = 5
	local MAX_SEGMENT_OFFSET = 5
	local BOLTS_PER_STRIKE = 20
	local MAX_STRIKES = 5
	local STRIKE_WAIT_INTERVAL = 0.2
	
	local bolts = {} -- Keeps references of all the created bolt segments
	
	local function makeStreak(direction)
		local nodes = {}
		
		-- First we need to get all our nodes
		for i = 1, BOLT_SEMGENTS + 1, 1 do -- We need n + 1 nodes for the bolt segments
			local newNode = position + direction/i
			
			if i > 1 then -- We want the first one to stick to the origin
				local range = {-MAX_SEGMENT_OFFSET, MAX_SEGMENT_OFFSET}
				local randomOffest = Vector3.new(math.random(unpack(range)), math.random(unpack(range)), math.random(unpack(range)))
				newNode = newNode + randomOffest
			end
			
			nodes[i] = newNode
			print("New node:", newNode)
	
			--local p = Instance.new("Part", workspace)
			--p.Anchored = true
			--p.Position = newNode
		end
		
		-- Now we need to create our cosmetic bolts:
		for i, currentNode in ipairs(nodes) do
			if i > BOLT_SEMGENTS then break end
			local nextNode = nodes[i + 1]

			local costmeticBolt = self.extraAssets.Bolt:Clone()
			local length = (nextNode - currentNode).magnitude
			
			
			local newSize = Vector3.new(costmeticBolt.Size.X, costmeticBolt.Size.Y, length)
			local newCF = CFrame.new(currentNode, nextNode) * CFrame.new(0, 0, -length/2)
			
			costmeticBolt.Parent = workspace
			costmeticBolt.Size = newSize
			costmeticBolt.CFrame = newCF
			
			table.insert(bolts, costmeticBolt)
		end
	end
	
	local function activateStrike()
		for i = 1, BOLTS_PER_STRIKE, 1 do
			local direction = Vector3.new(math.random(-1000, 1000), math.random(-1000, 1000), math.random(-1000, 1000)).unit * MAX_DISTANCE
			makeStreak(direction)
		end
		
		wait(STRIKE_WAIT_INTERVAL)
		
		for _, v in pairs(bolts) do
			v:Destroy()
		end
	end
	
	for i = 1, MAX_STRIKES ,1 do
		activateStrike()
	end
end

return Sub

Any tips on how I can improve the performance of my code is appreciated!

Is the bulk of the code ran on client or server side? I’m not extremely skilled with scripting, but I know that replicating anything visual to clients instead of having the server all the work makes things less laggy.

Sorry if I couldn’t help more.

1 Like

It’s all done on the server side. I need it to be as optimised as possible so that the server can do as many of these as possible simultaneously without noticable frame drops.

Having the clients handle the cosmetic/visual, and computational aspects of the program will save processing power on the server side. All the checks of course need to be done by the server, but I would leave the visual aspects for the clients to do.

Anything like creating lightning bolts, Tweening, CFraming, is better on the server performance-wise if they’re created or calculated by the client. Client visual management isn’t solely for Guis alone.

@Muoshoob explained this a little better than I could over here

Always try to reduce the workload of the server when do things like this, Make the client render the visual component and let the server do the rest, in most cases visual effects should be done on the client.