How should I approach enemy spawns?

I want enemies to spawn in a specific location/s. My main server script handles a Heartbeat game loop which looks for ready players, loads them on randomly selected map, etc.
My question is: How should I integrate the enemy spawning function within that main script, so it doesn't interrupt anything and doesn't yield until the spawning loop is done under certain condition (like "game over")?

Here is a bit of the main script:

Services.RunService.Heartbeat:Connect(function()
	if GameStatus.Intermission == true or GameStatus.inProgress == true then return end
	
	GameStatus.Intermission = true
	print('Intermission...')
	
	repeat -- Don't start the game until enough Players pressed 'Ready'
		if Counts:GetAttribute('isReadyCount') < GameSettings.PlayersNeeded then
			warn('Not enough players to start the game')	
		end
		task.wait(1)
	until Counts:GetAttribute('isReadyCount') >= GameSettings.PlayersNeeded
	
	print('Enough players...')
	task.wait(3)
	
	GameStatus.inProgress = true
	GameStatus.Intermission = false
	print('Game is starting, Intermission ended...')
	
	-- Random map is being chosen
	local Maps = (Services.ServerStorage.Maps):GetChildren()
	local RandomMap = Maps[math.random(1, #Maps)]
	local ClonedMap = RandomMap:Clone()
	ClonedMap.Parent = game.Workspace
	
	-- Send ready Players to the game
	local iCount = 0
	for i, Player in Services.Players:GetPlayers() do
		if Player == isReadyList[Player.UserId] then
			PlayingList[Player.UserId] = Player
			iCount += 1
			Player.Character:PivotTo(SpawnOrigin.CFrame)
			local Sword = (Weapons.Melee.ClassicSword):Clone()
			Sword.Parent = Player.Backpack
			Services.ReplicatedStorage.Events.Server.ChangeCamera:FireClient(Player)
		end
	end
	Counts:SetAttribute('PlayingCount', iCount)
end)

My main idea was to use task.spawn() or coroutine with function written like shown below, but wouldn’t that just loop forever, even when I coroutine.yield it? If I decide to break the loop, wouldn’t that mean I can’t use that loop anymore because its broken?

function spawnEnemies()
	local Enemies = Services.ReplicatedStorage.Enemies:GetChildren()
	while true do
		local randomEnemy = Enemies[math.random(1, #Enemies)]:Clone()
		randomEnemy.Parent = game.Workspace
		randomEnemy:PivotTo(EnemySpawnOrigin.CFrame)
		task.wait(1)
	end
end

What should be my ideal take here? Any help :pray:


local Spawns = workspace.Debris --Put this variable into what ur MAP's Folder of enemy spawns parts
local EnemyAmount = 10
local Enemy = workspace.Debris --put this to what ur enemy model actually is

for i = 1, EnemyAmount do
	local Cloned = Enemy:Clone()
	Cloned:PivotTo(Spawns:GetChildren()[math.random(1, #Spawns:GetChildren())])
	
	Cloned.Parent = workspace.Debris --Put this to where u want ur enemy to be parented at
end

this may not work exactly with ur script, but this should be enough to give u idea

1 Like

also, doing

local Stop = false
while true do
	
	if Stop == true then
		break
	end
	task.wait()
end

doing break wont mean u cant use that loop anymore, just call the function again and it will loop again

Thats good and all, but how would I call that function inside my main? I just call it when the game starts and listen to when the game ends - if so, break the loop? I suppose its not recommended to coroutine.create(function()) each time.

1 Like

I might not know or understand something here

if i understand correctly, ure afraid of breaking the loop thinking it will never work again, but calling the function u made will cause it to loop again

function spawnEnemies()
	local Enemies = Services.ReplicatedStorage.Enemies:GetChildren()
	while true do
		local randomEnemy = Enemies[math.random(1, #Enemies)]:Clone()
		randomEnemy.Parent = game.Workspace
		randomEnemy:PivotTo(EnemySpawnOrigin.CFrame)
		task.wait(1)
	end
end

spawnEnemies() -- type this whenever u wanna loop again

but i believe it would be better to use for i = 1, AmountOfEnemyYouWant do, instead of while true do

The script I’ve sent in the post is cropped. After loading players there is a bunch of other stuff, like waiting for all players to die, waiting if game ends somehow, etc. If I want to add a while loop to script, my code below won’t run unless the loop was broken, so checking for certain conditions won’t happen because of this. My problem lies more on a how do I run my loop simultaneously without interrupting my main script rather than how do I spawn enemies.
Hope that makes sense…

OOh i think i get now, what u want is for the function to run independently so that i wont stop ur main function from running through? if thats the case, use this

function spawnEnemies()
	local Enemies = Services.ReplicatedStorage.Enemies:GetChildren()
	while true do
		local randomEnemy = Enemies[math.random(1, #Enemies)]:Clone()
		randomEnemy.Parent = game.Workspace
		randomEnemy:PivotTo(EnemySpawnOrigin.CFrame)
		task.wait(1)
	end
end

task.spawn(spawnEnemies)

--other stuff

the other stuff will still continue while the spawnenemies also running , meaning there will be multiple running functions without stopping anything

task.spawn(function()
	--blablabla
end)

task.spawn spawns another thread so its independent, i hope that helps

So task.spawn() will be fine? And just basicly, if I want to break it, I can call the function again without any problems?

function spawnEnemies()
	local stopSpawning = false
	local Enemies = Services.ReplicatedStorage.Enemies:GetChildren()
	
	while true do
		if stopSpawning then break end
		local randomEnemy = Enemies[math.random(1, #Enemies)]:Clone()
		randomEnemy.Parent = game.Workspace
		randomEnemy:PivotTo(EnemySpawnOrigin.CFrame)
		task.wait(1)
	end
end

task.spawn(spawnEnemies)

This would break the loop if stopSpawning is set to true?

yea, break it whenever u want, and call it again using task.spawn(spawnEnemies) , its like u are walking on one road, then theres 2 roads, u cloned urself so both u and ur clone can go each of the road, idk if i explained it well

yea, but i recommend putting the local stopSpawning = false way above outside the function, so u can just do

local stopSpawning = false
function spawnEnemies()
	stopSpawning = false
	local Enemies = Services.ReplicatedStorage.Enemies:GetChildren()
	
	while true do
		if stopSpawning == true then break end
		local randomEnemy = Enemies[math.random(1, #Enemies)]:Clone()
		randomEnemy.Parent = game.Workspace
		randomEnemy:PivotTo(EnemySpawnOrigin.CFrame)
		task.wait(1)
	end
end

task.spawn(spawnEnemies)

task.wait(5) -- example
stopSpawning = true
1 Like

Thank you very much! Last question about the task library… the task.spawn() will just end when the code inside passed function finishes? Asking, because I don’t want anything running in the background unnecessarily.

yes it will just end, it wont go the main function, its like a seperate script, it works on its own and even if it causes an error, it wont affect the main function u been running task.spawn() is just a branch not the body of the tree

1 Like

Thank you! That was very helpfull!

1 Like