Tips on Improving Performance

In my game, once there’s too many zombies the framerate starts dropping horrendously and the CPU struggles so I want to find some solutions to this. Aside from the obvious reducing the number of zombies I want to know if this may be an error in my scripting since I see these other tower defense games with hundreds of enemies walking through and still maintaining bearable frames even on mobile.

Zombie Code:

(target is an ObjectValue)

local function Zombie()
    while task.wait(1/6) do
		-- Zombie Targeting Code
		if target.Value ~= nil and target.Value.PrimaryPart ~= nil then
			humanoid:MoveTo(target.Value.PrimaryPart.Position)

			local ray = castRay()

			if ray and ray.Instance and canAttack then
				attack(ray)
			end

			if target.Value.Name and target.Value:FindFirstChild("Humanoid") and target.Value.Humanoid.Health <= 0 then
				wait(5.5)
			
				local name = target.Value.Name
				target.Value = workspace:FindFirstChild(name)
			end
		else
			zombie:Destroy()
		end
	end
end

workspace.DescendantAdded:Connect(function(instance)
	if instance:HasTag("Zombie") then
		task.spawn(Zombie, instance)
	end
end)

If the reason for all the lag is the humanoids, should I replace them with something else?
I’ll provide extra details if needed

(p.s i think its probably because im starting a new while wait loop for every zombie but quote me on this)

16 Likes

instead of spawning a new loop for every zombie that spawns, i believe you can use just one loop to handle all zombies via. the use of collectionservice

should be task.wait()

11 Likes

that was a small little workaround because wait() has throttling or something since after the player dies the zombies dont chase anymore

So how do you think i should go doing this? does CollectionService:getInstanceAddedSignal fire when an object with the tag is cloned?

5 Likes

Turn off CanQuery And CanTouch properties on parts that don’t need it and also don’t use Humanoids they’re way too unoptimized and one last thing, don’t use instance values, use attributes instead.

5 Likes

Use task.wait() instead of wait(), task.wait() does not throttle.

7 Likes

I would not use wait() functions here at all, but instead have a table of zombies and their data like targets, states and timers that would be reset every x miliseconds (using RunService for timer updates) triggering state/target changes.

5 Likes

hmm that could work but the reason why i use while task.wait(1/6) is because i want the script to only run 6 times a second as opposed to RunService.Heartbeat which runs mostly at 60 times a second

1 Like

That’s why I mentioned timer variables that you can flip with frequency you want.

Run.Heartbeat:Connect(function(_dt)
	ProxTestTime -= _dt
	
	if ProxTestTime <= 0  then
		ProximitiesTest()
		ProxTestTime = ProxTestFreq
	end
end)
4 Likes

One thing I noticed which is also creating lag but a different kind aka ping lag is your server receive, you are stressing the server way too much, consider moving some stuff to the client with server sanity checks, as well as check for memory leaks you probably have a few

5 Likes

how do you check for memory leaks?

2 Likes

You can disable humanoid states to gain a little bit of performance, also you can disable collisions on parts and replace them with hitbox like stuff, for gameplay, you should create enemy-limiter, it’s psychological trick

See, most of the time players will have enough power to turn 100 enemies into dust quick, instead of doing more and more each wave, scale enemies to like 50 or soo and increase their size and HP

3 Likes

If using physic to move some npc recommend maximun 100 NPC. Never do loop per zombie just enough use 1 loop control everything also make sure turn CollisionGroup each zombie. There way using tween but i still not made it

Example NPCs use physic to move - No Pathfinding and no attack
local function spawn_zombie(Name,Position)
local Zombie=game.ServerStorage[Name]:Clone()
	
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Seated,false) -- These all mostly state can disabled
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Flying,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.PlatformStanding,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.StrafingNoPhysics,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Ragdoll,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Physics,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.FallingDown,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Swimming,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Climbing,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp,false)
Zombie.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Running,false)

Zombie.Parent=workspace.npc
Zombie:MoveTo(Position)
Zombie.Humanoid:AddTag('Zombie')
for i,v in pairs(Zombie:GetChildren())do
if v:IsA'BasePart'then
v:SetNetworkOwner(nil)
end
end
end

function search_to_attack(v)
local list=workspace:children() -- i can say it's better to put NPC another folder
local d=200 -- Distance
local temp,human,temp2,torso
for x=1,#list do
temp2=list[x]
if temp2:FindFirstChild'Humanoid'then
temp=temp2:findFirstChild'HumanoidRootPart'
human=temp2:findFirstChild'Humanoid'
if temp~=nil and human.Health>0 then
if(temp.Position-v.Torso.Position).magnitude<d then
torso=temp
d=(temp.Position-v.Torso.Position).magnitude
end end end end return torso end

task.delay(0,function()
while task.wait(.5)do -- Used follow someone and i recommend loop just single
for i,v in pairs(game.CollectionService:GetTagged'Zombie')do
if v then
local target=search_to_attack(v)
if target~=nil then 
v:MoveTo(target.Position,target)
end
end
end
end
end)

-- vvv Not important just spawn zombie vvv
local zombiecount=0
task.delay(0,function()
while task.wait(.1)do
zombiecount+=1
print(zombiecount)
spawn_zombie( -- Time to spawn 1 NPC
 'Zombie', -- Name of zombie in ServerStorage what ever
 Vector3.new(math.random(-50,50),3,math.random(-50,50) -- Spawn Position
))
end
end)
5 Likes

well for one if you have any tables on the server make sure to remove players data from it when they leave another is leaving instances connected, if you can you should always disconnect when OR if u only need it connected once consider using :Once. You can check if you have data leaks by opening lua heap and seeing if it is constantly rising with being GCed, u can also check if the amount of instances in game keep rising. Garbage Collection and Memory Leaks in Roblox - What you should know, also instead of making a loop to keep updating move to consider using the roblox pathfinding or you can use renderstepped instead, but u would want to do that with Collection service, u could also cache all the zombies in a table then see which zombies are in range and only send that moving data to them :smile:

2 Likes

Also try to avoid using while loops, try to opt for something else like runservice, and why are u using instances values what are you even storing in them?

2 Likes

i used the object values to store the zombie’s target but i realized its not optimal so i have changed it now

my solution its better about use how does humanoidrootpart its anchored then move use cframe way better rather than use physic moving it does use alot cpu

My example bullet tracing render in 1 loop.... script just too mess but not too long

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.