How can I heavily reduce the bandwidth of this Enemy System foundation code?

Hello developers, I am currently working on a tower defense game and I was wondering how I can reduce the Recv usage when handling hundreds if not thousands of enemies? I was considering trying out client replication but my knowledge only knows so much lol. They don’t use humanoids so thats already a plus point.

My code:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")

local BezierPath = require(game.ReplicatedStorage.Modules.BezierPath)
local Enemies = ReplicatedStorage.Common.Enemies

local map = workspace.Map:FindFirstChild("TestMap")
local EnemyFolder = workspace.InGameAssets.Enemies

local nodesFolder = map:WaitForChild("Nodes")

local nodes = {}
for _, node in ipairs(nodesFolder:GetChildren()) do
	if node:IsA("BasePart") then
		table.insert(nodes, node)
	end
end

table.sort(nodes, function(a, b)
	return tonumber(a.Name) < tonumber(b.Name)
end)

local waypoints = {}
for _, node in ipairs(nodes) do
	table.insert(waypoints, node.Position)
end

local path = BezierPath.new(waypoints, 100)

path:VisualizePath()

local function moveEnemy(enemy)
	local root = enemy:WaitForChild("HumanoidRootPart")
	local enemyStats = require(ReplicatedStorage.Common.Enemies.Stats[enemy.Name])

	root.Anchored = true

	local baseSpeed = enemyStats.BaseSpeed or 10
	local pathLength = path:GetPathLength()
	local duration = pathLength / baseSpeed

	local sideOffset = Random.new():NextNumber(-0.8, 0.8)
	
	local _, size = enemy:GetBoundingBox()
	local heightOffset = size.Y / 3

	local startTime = tick()
	local connection

	local UPDATE_INTERVAL = 1/30
	local lastUpdate = 0

	connection = RunService.Heartbeat:Connect(function(dt)
		lastUpdate += dt
		if lastUpdate < UPDATE_INTERVAL then return end
		lastUpdate = 0
		local elapsed = tick() - startTime
		local progress = math.clamp(elapsed / duration, 0, 1)

		local cframe = path:CalculateUniformCFrame(progress)
		local offsetCFrame = cframe * CFrame.new(sideOffset, heightOffset, 0)
		root.CFrame = offsetCFrame

		if progress >= 1 then
			connection:Disconnect()
			enemy:Destroy()
			ReplicatedStorage.Common.Remotes.EnemyDestroyed:FireAllClients()
		end
	end)
end

local function spawnEnemy(amount, delay)
	for i = 1, amount do
		local enemy = Enemies.Models.Undead:Clone()
		enemy.Parent = EnemyFolder
		enemy.HumanoidRootPart:SetNetworkOwner(nil)
		
		ReplicatedStorage.Common.Remotes.EnemySpawned:FireAllClients()

		task.spawn(function()
			moveEnemy(enemy)
		end)

		task.wait(delay)
	end
end

spawnEnemy(1000, 0.1)

This is the BezierPath module I used.

1 Like

I know I have statements that use RemoteEvents but removing them didn’t help that much

1 Like

Are you using humanoids?
(mb didn’t read the post properly)


1 Like

@devvres, don’t actually move the enemy instances on the server. Instead, send discretized positions (or waypoints, however granular you’d like your steps) over and have the clients compute the interpolation for each enemy.

1 Like

Will definitely try that out, thanks! I’ll let you know if I have any queries

2 Likes