Td game enemy optimization

even tho the client’s not doing any rendering, or calculations, it still horribly lags?
for context, i am spawning 1000 zombies, the zombie in question being a part. (before the wave started i had a solid 60+ fps)

here’s my code


function mob.Configure(mob, pathProgressed)
	local mobToCopy = game.ServerStorage.Zombies:WaitForChild(mob.Name, math.huge)
	
	local statsFolder = mob.Stats
	local physicsFolder = mob.physics
	local fakeStats = mobToCopy:FindFirstChild("Stats")
	
	local mobSpeed = Instance.new("NumberValue")
	local mobHealth = Instance.new("NumberValue")
	local mobMHealth = Instance.new("NumberValue") --max health
	mobSpeed.Name = "Speed"
	mobHealth.Name = "Health"
	mobMHealth.Name = "MaxHealth"
	
	mobSpeed.Parent = statsFolder
	mobHealth.Parent = statsFolder
	mobMHealth.Parent = statsFolder
	
	mobSpeed.Value = fakeStats.Walkspeed.Value
	mobHealth.Value = fakeStats.Health.Value
	mobMHealth.Value = fakeStats.MaxHealth.Value
	
	local distanceVar = Instance.new("NumberValue")
	distanceVar.Name = "PathProgressed"
	distanceVar.Parent = mob
	distanceVar.Value = pathProgressed or 0
	
	mob:SetAttribute("SpawnTime", tick())
	mob:SetAttribute("ElapsedTime", 0)
	mob:SetAttribute("LastSpeed", mobSpeed.Value)
	
	EnemyManager:AddEnemy(mob)
end

function updateEnemy(enemy, deltaTime)
	if not (enemy and enemy.PrimaryPart) then
		return false
	end

	local Path = Path
	local Speed = enemy.Stats.Speed.Value
	local TotalTime = Path:GetPathLength() / Speed
	local distanceVar = enemy.PathProgressed.Value
	--local offset = Vector3.new(0, enemy["Left Leg"].Size.Y - 0.11, 0)
	--local raloffset = enemy.RandomOffset

	enemy:SetAttribute("ElapsedTime", enemy:GetAttribute("ElapsedTime") + deltaTime)

	local T = enemy:GetAttribute("ElapsedTime") / TotalTime

	if Speed ~= enemy:GetAttribute("LastSpeed") then
		local currentDistance = T * Path:GetPathLength()
		TotalTime = Path:GetPathLength() / Speed
		T = currentDistance / Path:GetPathLength()
		enemy:SetAttribute("ElapsedTime", T * TotalTime)
		enemy:SetAttribute("LastSpeed", Speed)
	end

	--print(tonumber(string.format("%.5f", Path:GetPathLength() * T)))
	--distanceVar.Value = tonumber(string.format("%.5f", Path:GetPathLength() * T))

	-- pos update
	local cframe = Path:CalculateUniformCFrame(T)
	enemy.PrimaryPart.CFrame = (cframe)

	if T >= 1 then
		return false
	end

	return true
end

function mob.Spawn(mobName, howMany, spawnrate, start, pathprogress)
	local isthere = game.ServerStorage.Zombies:FindFirstChild(mobName)

	if isthere then
		for i = 1, howMany do
			
			local mobS = game.ServerStorage.sampleZombie:Clone()
			mobS.Name = mobName
			mobS.PrimaryPart.CFrame = start.CFrame
			mobS.Parent = workspace.Game.Zombies
			mob.Configure(mobS, pathprogress)
			
			game.Workspace["Map"].spawnloc["bass.wav"]:Play()
			task.wait(spawnrate)
		end
	end
end

runService.Stepped:Connect(function(_, deltaTime)
	for _, enemy in ipairs(EnemyManager.Enemies) do
		updateEnemy(enemy, deltaTime)
	end
end)

The client not making the calculations doesn’t necessarily mean it will run smoothly since you have a lot of incoming data from the server and rendering a lot of enemies.

I’d suggest checking out Parallel Luau and if you’re using humanoids, make sure to disable unnecessary Humanoid states.

Thanks, i’ll check that out ! Also no, i’m not using any humanoids, since i’m using BezierPath to move them.