Hey, I’m Deathbandss and I’m making a tower defense game called Galactic Reckoning and I’ve made an entity system that can handle 500+ enemies before fps drop
I want to know if theres any way I can make it so that I can optimize my enemies to handle more than just 500-600.
and also if theres a way for me to fix these gaps.
If you have any advice,tips,or suggestions let me know.
(also I wont be using humanoids at all for this so please dont suggest walkto or pathfinding service!)
Server Sided Script:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Nodes = workspace.BaseplateMap.Nodes
local Modules = ReplicatedStorage.Modules
local Events = ReplicatedStorage.Event
local EnemyInfo = require(Modules.EnemyInfo)
local Percision = 10^2
local counter = 0
local StartTime = workspace:GetServerTimeNow()
local TickRate = 1/20
local AccumlatorDelta = 0
local enemy = {}
local currentenemies = {}
function enemy.Spawn(name)
counter += 1
local EnemyTable = {
["Name"] = name;
["ID"] = counter;
["ModelID"] = EnemyInfo[name].ModelID;
["EnemySpawnTime"] = tick(),
["CFrame"] = CFrame.new(workspace.BaseplateMap.Start.Position,workspace.BaseplateMap.Nodes[1].Position);
["Node"] = 1;
["Speed"] = EnemyInfo[name].Speed;
["Health"] = EnemyInfo[name].Health;
["MaxHealth"] = EnemyInfo[name].MaxHealth;
["Buffs"] = EnemyInfo[name].Buffs
}
currentenemies[counter] = EnemyTable
local ClientTable = {
["CodedVector2"] = Vector2int16.new(EnemyTable.ModelID,EnemyTable.Node)
}
Events.SpawnEnemy:FireAllClients(ClientTable)
coroutine.wrap(ServerSidedMovement)(counter)
end
function ServerSidedMovement(EnemyID)
local Connection
local EnemyTable = currentenemies[EnemyID]
if EnemyTable == nil then return end
local Start = workspace.BaseplateMap.Start
for index,value in ipairs(Nodes:GetChildren()) do
local ReachedWaypoint = false
local NextWaypoint = Nodes:FindFirstChild(tostring(index))
local CurrentWaypoint = Nodes:FindFirstChild(tostring(index - 1))
if CurrentWaypoint == nil then
CurrentWaypoint = Start
end
local timehappened = 0
local function MoveEnemy(DeltaTime)
if CurrentWaypoint ~= Nodes:FindFirstChild(tostring(#Nodes:GetChildren())) and ReachedWaypoint == false then
timehappened += DeltaTime
local Speed = EnemyTable.Speed
local distance = (CurrentWaypoint.Position - NextWaypoint.Position).Magnitude
local Time = distance/Speed
local alpha = (timehappened/Time) % 1
EnemyTable.CFrame = CurrentWaypoint.CFrame:Lerp(NextWaypoint.CFrame,alpha)
EnemyTable.CFrame = CFrame.new(EnemyTable.CFrame.Position,NextWaypoint.Position)
if alpha >= 0.989 then
ReachedWaypoint = true
Connection:Disconnect()
end
end
task.wait(0.024)
end
Connection = RunService.Heartbeat:Connect(MoveEnemy)
repeat task.wait(0.2) until ReachedWaypoint
end
Connection:Disconnect()
Events.EnemyDie:FireAllClients(EnemyID,EnemyTable.Health,true)
end
return enemy
Client Sided:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local EnemiesFolder = workspace.Enemy
local RunService = game:GetService("RunService")
local Percision = 10^2
local players = game:GetService("Players")
local player = players.LocalPlayer
local GUI = player.PlayerGui
local LastUpdate = workspace:GetServerTimeNow()
local Nodes = workspace.BaseplateMap.Nodes
local Enemies = ReplicatedStorage.Enemies
local Events = ReplicatedStorage.Event
local counter = 0
--[[function quadBezier(t, p0, p1, p2)
return (1 - t)^2 * p0 + 2 * (1 - t) * t * p1 + t^2 * p2
end]]
function ClientEnemyMovement(Enemy,EnemySpawnTime)
local Start = workspace.BaseplateMap.Start
local Connection
if Enemy == nil or Enemy.PrimaryPart == nil then return end
local EnemySpawnTime = EnemySpawnTime
for index,value in ipairs(Nodes:GetChildren()) do
local ReachedWaypoint = false
local NextWaypoint = Nodes:FindFirstChild(tostring(index))
local CurrentWaypoint = Nodes:FindFirstChild(tostring(index - 1))
if CurrentWaypoint == nil then
CurrentWaypoint = Start
end
local timehappened = 0
local function MoveEnemy(DeltaTime)
if CurrentWaypoint ~= Nodes:FindFirstChild(tostring(#Nodes:GetChildren())) and ReachedWaypoint == false then
timehappened += DeltaTime
local Speed = Enemy:GetAttribute("Speed")
local distance = (CurrentWaypoint.Position - NextWaypoint.Position).Magnitude
local Time = distance/Speed
local alpha = (timehappened/Time) % 1
if Enemy.PrimaryPart == nil then
Connection:Disconnect()
end
Enemy.PrimaryPart.CFrame = CurrentWaypoint.CFrame:Lerp(NextWaypoint.CFrame,alpha)
Enemy.PrimaryPart.CFrame = CFrame.new(Enemy.PrimaryPart.Position,NextWaypoint.Position)
if alpha >= 0.989 then
ReachedWaypoint = true
Connection:Disconnect()
end
end
task.wait(0.024)
end
Connection = RunService.Heartbeat:Connect(MoveEnemy)
repeat task.wait(0.2) until ReachedWaypoint
end
Connection:Disconnect()
end
Events.SpawnEnemy.OnClientEvent:Connect(function(ClientTable)
counter += 1
if not EnemiesFolder:FindFirstChild(counter) then
for i,v in pairs(Enemies:GetChildren()) do
if v:GetAttribute("ModelID") == ClientTable.CodedVector2.X then
local lilbuddy = Enemies[tostring(v)]:Clone()
for i, v in pairs(v:GetChildren()) do
if v:IsA("BasePart") or v:IsA("MeshPart") then
v.CollisionGroup = "Mobs"
end
end
lilbuddy:SetAttribute("EnemyID",counter)
lilbuddy.Parent = EnemiesFolder
ClientEnemyMovement(lilbuddy)
end
end
end
end)