Need help limiting number of zombies spawned at a time

I have a script that spawns a few “FakeZombies”, the FakeZombies break barriers and then spawn the actual “Zombie”

In the FakeZombie and Zombie model there is a script that changes a value in replicated storage called ZombiesCount by -1, the main script checks that value so it knows once the player has killed all the zombies.

I’ve tried making loops that check the number of Zombies and FakeZombies spawned at a time so it can add a limit to amount spawned. I want to make it so if about 25 Zombies/FakeZombies are spawned in then it will stop spawning more until one has been killed and there is room.

Here is my script, any help is appreciated!


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ZombiesCount = ReplicatedStorage:WaitForChild("ZombiesCount")

local Folder = workspace.ZombiesFolder -- Folder

-- Configuration
local IntermissionDuration = 8 -- time until next round
local InitialZombiesCount = 5
local ZombiesCountIncrement = 2
local maxZombiesCount = 3

-- Variables
local zombieHealth = 80
local zombieSpeed = 8
local roundNumber = 0
local zombiesRemaining = 0


--When a round starts, teleport all players that are dead to the map spawn.

local DeadPart = workspace:FindFirstChild("Dead")
local AlivePart = workspace:FindFirstChild("Alive")

local function teleportPlayers()
	for _, player in pairs(game.Players:GetPlayers()) do
		local character = player.Character
		if character then
			local humanoid = character:FindFirstChildOfClass("Humanoid")
			if humanoid then
				local distance = (DeadPart.Position - humanoid.RootPart.Position).Magnitude
				if distance <= 10 then
					humanoid.RootPart.CFrame = AlivePart.CFrame
				end
			end
		end
	end
end



function startRound()
	teleportPlayers()
	roundNumber = roundNumber + 1
	zombiesRemaining = InitialZombiesCount + ZombiesCountIncrement * (roundNumber - 1)
	zombieHealth = zombieHealth * 1.25
	ZombiesCount.Value = zombiesRemaining
	ReplicatedStorage.RoundNumber.Value = roundNumber

	-- Spawn zombies
	for i = 1, zombiesRemaining do
		local Folder_Children = Folder:GetChildren()
		local ZombieClone = ReplicatedStorage.FakeZombie:Clone()
		ZombieClone.Humanoid.MaxHealth = zombieHealth
		ZombieClone.Humanoid.Health = zombieHealth
		ZombieClone.Humanoid.WalkSpeed = zombieSpeed
		ZombieClone.Parent = workspace.ZombiesFolder
		task.wait(1)
	end
	
	-- Check if round is over
	while zombiesRemaining > 0 do
		task.wait(1)
		zombiesRemaining = ZombiesCount.Value
	end
	
	-- If round is over then start next
	if zombiesRemaining <= 0 then
		task.wait(IntermissionDuration)
		startRound()
	end

	-- Start next round
end

-- Start first round
task.wait(IntermissionDuration)
startRound()
teleportPlayers()

ZombiesCount is a NumberValue. The neat thing about those, is that they are objects that have events that you can wait for. But before I show you how to do that, I have an advice:

Your startRound() function is recursive. That means it executes itself at the end. Normally this would not be a problem, but this is generally discouraged for anything else than really simple functions. It can cause a lot of problems if you are not careful and will be slowly leaking memory.

Granted, probably not much, but there are better ways to solve this. Let me introduce to you coroutines. By splitting Your script into logical chunks, you can have them working along side each other. And you can replace recursive function with an infinite loop.

Here is the code:

-- add this to configuration
local maxZombiesOnMap = 25

function startRound()
	teleportPlayers()
	roundNumber = roundNumber + 1
	zombiesRemaining = InitialZombiesCount + ZombiesCountIncrement * (roundNumber - 1)
	zombieHealth = zombieHealth * 1.25
	ZombiesCount.Value = zombiesRemaining
	ReplicatedStorage.RoundNumber.Value = roundNumber

	

	-- Spawn zombies 
	-- here is the first coroutine
	-- this coroutine will handle the zombie spawning
	task.spawn(function()
		--create new variable, as zombiesRemaining will be overwritten by players killing zombies
		local zombiesToSpawn = zombiesRemaining
		local zombiesOnMap = 0
		while zombiesToSpawn > 0 do
			--this loop will pause the spawner, when there are too many zombies
			zombiesOnMap = ZombiesCount.Value - zombiesToSpawn
			while zombiesOnMap >= maxZombiesOnMap do
				ZombiesCount.Changed:Wait()
				-- double check, as changed event does not guarantee that the condition is met
				zombiesOnMap = ZombiesCount.Value - zombiesToSpawn
			end

			local Folder_Children = Folder:GetChildren()
			local ZombieClone = ReplicatedStorage.FakeZombie:Clone()
			ZombieClone.Humanoid.MaxHealth = zombieHealth
			ZombieClone.Humanoid.Health = zombieHealth
			ZombieClone.Humanoid.WalkSpeed = zombieSpeed
			ZombieClone.Parent = workspace.ZombiesFolder
			-- update the counter
			zombiesToSpawn -= 1
			task.wait(1)
		end
	end)

	-- Check if round is over
	while zombiesRemaining > 0 do
		--task.wait(1)
		--this is better
		ZombiesCount.Changed:Wait()
		zombiesRemaining = ZombiesCount.Value
	end
	
	--[[
	this is bad, as recursive function like this will cause a memory leak
	if zombiesRemaining <= 0 then
		task.wait(IntermissionDuration)
		startRound()
	end--]]


end

-- Start first round
-- loop the function here
-- you may use coroutine if you plan to add more statements later in the script
task.spawn(function() -- this is optional, if while true loop is the last thing in the script, this may be removed
	while true do
		task.wait(IntermissionDuration)
		startRound()
		--no need to teleport players, as you do it inside the startRound() function anyway
	end
end)

1 Like

Thank you so much! I’ve seen people use coroutines but i haven’t looked into them yet. I’ll definitely check them out since they seem really helpful!

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