Round System Stopping

Hello! I am making a round system for my game and it works! I am trying to achieve something so that the round stops when all players have died/disconnected but have no clue on where to start. Any help is appreciated!

Script:

local Obbies = game.ReplicatedStorage.Obbies
	local PlayersInGame = {}
	local playersDisconnected = {}
	local inRound = false
	local Status = game.ReplicatedStorage.Values.Status

	local EasyObby = nil
	local EasyNum = math.random(1, 3)

	if EasyNum == 1 then
		EasyObby = Obbies.Easy["Cake Walk - Easy"]:Clone()
		EasyObby.Parent = game.Workspace
		EasyObby:SetPrimaryPartCFrame(CFrame.new(-636.45, 51.9, -110.35))
	elseif EasyNum == 2 then
		EasyObby = Obbies.Easy["One Jump - Easy"]:Clone()
		EasyObby.Parent = game.Workspace
		EasyObby:SetPrimaryPartCFrame(CFrame.new(-636.45, 51.9, -110.35))
	elseif EasyNum == 3 then
		EasyObby = Obbies.Easy["Legit Just a Cake Walk - Easy"]:Clone()
		EasyObby.Parent = game.Workspace
		EasyObby:SetPrimaryPartCFrame(CFrame.new(-636.45, 51.9, -110.35))
	end

	for i, v in pairs(game.Players:GetPlayers()) do
		v.PlayerInGame.Value = true
		v.Character.HumanoidRootPart.CFrame = EasyObby.PrimaryPart.CFrame
		table.insert(PlayersInGame, v)
	end

	local MediumNum = math.random(1, 3)
	local MediumObby = nil
	if MediumNum == 1 then
		MediumObby = Obbies.Medium["Lava Jumps - Medium"]:Clone()
		MediumObby.Parent = game.Workspace
		MediumObby:SetPrimaryPartCFrame(CFrame.new(-520.95, 51.9, -110.35))
	elseif MediumNum == 2 then
		MediumObby = Obbies.Medium["Big Lava Jump - Medium"]:Clone()
		MediumObby.Parent = game.Workspace
		MediumObby:SetPrimaryPartCFrame(CFrame.new(-520.95, 51.9, -110.35))
	elseif MediumNum == 3 then
		local MediumObby = Obbies.Medium["WHAT?! HOW?! - Medium"]:Clone()
		MediumObby.Parent = game.Workspace
		MediumObby:SetPrimaryPartCFrame(CFrame.new(-520.95, 51.9, -110.35))
	end

	local HardNum = math.random(1, 3)
	local HardObby = nil
	if HardNum == 1 then
		HardObby = Obbies.Hard["Guesser - Hard"]:Clone()
		HardObby.Parent = game.Workspace
		HardObby:SetPrimaryPartCFrame(CFrame.new(-577.55, 51.9, -110.35))
	elseif HardNum == 2 then
		local HardObby = Obbies.Hard["Guesser 2.0 - Hard"]:Clone()
		HardObby.Parent = game.Workspace
		HardObby:SetPrimaryPartCFrame(CFrame.new(-577.55, 51.9, -110.35))
	elseif HardNum == 3 then
		HardObby = Obbies.Hard["Extreme Lava Jumps - Hard"]:Clone()
		HardObby.Parent = game.Workspace
		HardObby:SetPrimaryPartCFrame(CFrame.new(-577.55, 51.9, -110.35))
	end	

	for i = 60, 0, -1 do
		if i > 1 then
			wait(1)
			Status.Value = "In Game: "..i.." seconds until game is over!"
		elseif i == 1 then
			wait(1)
			Status.Value = "In Game: "..i.." second until game is over!"
		else
			wait(1)
			Status.Value = "Time's up!"
			wait(1)
			for i, v in pairs(game.Players:GetPlayers()) do
				if v.PlayerInGame.Value == true then
					v.PlayerInGame.Value = false
					v.Character.Humanoid.Health = 0
					table.remove(PlayersInGame, table.find(PlayersInGame, v))
				end
			end
			EasyObby:Destroy()
			MediumObby:Destroy()
			HardObby:Destroy()
		end
	end

What if below the line of,

for i = 60, 0, -1 do

make another elseif statement to detect if Humanoid.Died. I’m not sure if it’ll work, but it’s worth a try. I could test and do the trial and error myself if you like, I just need to know where to put the script at/or a place file.

I’m pretty sure that would only work for 1 player. I’m trying to make it stop when all players have died.

1 Like

So then when someone has died, you should move them out of the PlayerInGame Table. Something like:

for i, v in pairs(game.Players:GetPlayers()) do
	
	v.PlayerInGame.Value = true
	v.Character.HumanoidRootPart.CFrame = EasyObby.PrimaryPart.CFrame
	table.insert(PlayersInGame, v)

	if v.Character.Humanoid.Died then
		
		table.remove(PlayersInGame, table.find(PlayersInGame, v))
		v.PlayerInGame.Value = false
	end
end

And then make another condition inside the for i = 60, 0, -1 do:

for i = 60, 0, -1 do

	if i > 1 then

			wait(1)
			Status.Value = "In Game: "..i.." seconds until game is over!"
		elseif i == 1 then

			wait(1)
			Status.Value = "In Game: "..i.." second until game is over!"
		elseif #PlayersInGame == 0 then
			
			Status.Value = "Everyone has died!"
			break
		else
			wait(1)
			Status.Value = "Time's up!"
			wait(1)
			for i, v in pairs(game.Players:GetPlayers()) do
				if v.PlayerInGame.Value == true then
					v.PlayerInGame.Value = false
					v.Character.Humanoid.Health = 0
					table.remove(PlayersInGame, table.find(PlayersInGame, v))
				end
			end
			EasyObby:Destroy()
			MediumObby:Destroy()
			HardObby:Destroy()
		end
	end
1 Like

I tried this just now and it still doesn’t stop the loop.

You could maybe send me a… place file, in pm?

You’d create a new connection for all players that have died. I’d do it kinda like this

local Players = game:GetService("Players")
	
local activePlayers = {}
local connections = {}
	
	
local function removePlayer(player)
	local playerFound = table.find(activePlayers, player)
	if (playerFound) then
		table.remove(activePlayers, playerFound)
        connections[player.UserId.."Left"]:Disconnect()
        connections[player.UserId.."Died"]:Disconnect()
	end	
end
	
-- When you get the players ready/teleporting them
for _, player in pairs(Players:GetPlayers()) do
	if (player) then
		local character = player.Character
		-- Making sure they're alive before we put them into the game
		if (character.Humanoid.Health > 0) then
			table.insert(activePlayers, player)
			
			-- Setting up connections to check if the player has died or left
			connections[player.UserId.."Left"] = player.AncestryChanged:Connect(function()
                removePlayer(player)
            end)
	
           connections[player.UserId.."Died"] = character.Humanoid.Died:Connect(function()
	           removePlayer(player)
           end)
		end
	
	end
end
	
local gameResult = "Game over"
-- Main game loop
for i = 60, 0, -1 do
	-- If time is up
	if (i == 0) then
		gameResult = "Time's up"
		break
	end
	
	if (#activePlayers == 0) then
		gameResult = "Everyone left or died"
		break
	end
	
	
	task.wait(1)
end
	
-- == CLEANING UP THE GAME
for _, remainingPlayer in pairs(activePlayers) do
	warn(gameResult)
	-- Reward players and do stuff
end
	
-- Cleaning up connections
for _, connection in pairs(connections) do
	connection:Disconnect()
end
table.clear(activePlayers)

1 Like

Yeah, you could just create a connection to eliminate the player. You could just make a function that handles connections and whether the player is in the round or not. Make sure to reset the connections though and reset any left over players at the end of the round.

local Services = {
	Players = game:GetService("Players");
	ReplicatedStorage = game:GetService("ReplicatedStorage");
};

local RoundComponents = {
	Participants = {};
	Connections = {};
	Settings = {
		inRound = false;
	};
	Status = Services.ReplicatedStorage:WaitForChild("Values").Status;
	Objects = {
		Obbies = Services.ReplicatedStorage:WaitForChild("Obbies");	
	};
	DefaultConnections = {
		"%s_Leaving";
		"%s_Death";
	};
};

local RoundFunctions;
RoundFunctions = {
	addConnection = function(name, connection)
		RoundComponents.Connections[name] = connection;
		return connection;
	end;
	removeConnection = function(name)
		local Connection = RoundComponents.Connections[name];
		Connection:Disconnect();
		RoundComponents.Connections[name] = nil;
	end;
	resetConnections = function(player)
		for i,v in pairs(RoundComponents.DefaultConnections) do
			local Format = string.format(v, player.Name);
			RoundFunctions.removeConnection(Format);
		end;
	end;
	resetPlayer = function(player, health)
		player.PlayerInGame.Value = false;
		if health then
			player.Character.Humanoid.Health = 0;
		end;
		table.remove(RoundComponents.Participants, table.find(RoundComponents.Participants, player));
		RoundFunctions.resetConnections(player);
	end;
	setupPlayer = function(player)
		local Character = player.Character;
		local Humanoid = Character and Character:FindFirstChildOfClass("Humanoid");
		if Character and Humanoid then
			player.PlayerInGame.Value = true;
			table.insert(RoundComponents.Participants, player);
			local connection1 = RoundFunctions.addConnection(player.Name.."_Leaving", player.AncestryChanged:Connect(RoundFunctions.resetPlayer(player)));
			local connection2 = RoundFunctions.addConnection(player.Name.."_Death", Humanoid.Died:Connect(RoundFunctions.resetPlayer(player)));
		else
			RoundFunctions.resetPlayer(player, false);
		end;
	end;
};

local EasyObby = nil;
local EasyNum = math.random(1, 3);

if EasyNum == 1 then
	EasyObby = RoundComponents.Objects.Easy["Cake Walk - Easy"]:Clone();
	EasyObby.Parent = workspace;
	EasyObby:SetPrimaryPartCFrame(CFrame.new(-636.45, 51.9, -110.35));
elseif EasyNum == 2 then
	EasyObby = RoundComponents.Objects.Easy["One Jump - Easy"]:Clone();
	EasyObby.Parent = workspace;
	EasyObby:SetPrimaryPartCFrame(CFrame.new(-636.45, 51.9, -110.35));
elseif EasyNum == 3 then
	EasyObby = RoundComponents.Objects.Easy["Legit Just a Cake Walk - Easy"]:Clone();
	EasyObby.Parent = workspace;
	EasyObby:SetPrimaryPartCFrame(CFrame.new(-636.45, 51.9, -110.35));
end;

for i, v in pairs(Services.Players:GetPlayers()) do
	RoundFunctions.setupPlayer(v);
	v.Character.HumanoidRootPart.CFrame = EasyObby.PrimaryPart.CFrame;
end;

local MediumNum = math.random(1, 3);
local MediumObby = nil;
if MediumNum == 1 then
	MediumObby = RoundComponents.Objects.Medium["Lava Jumps - Medium"]:Clone();
	MediumObby.Parent = workspace;
	MediumObby:SetPrimaryPartCFrame(CFrame.new(-520.95, 51.9, -110.35));
elseif MediumNum == 2 then
	MediumObby = RoundComponents.Objects.Medium["Big Lava Jump - Medium"]:Clone();
	MediumObby.Parent = workspace;
	MediumObby:SetPrimaryPartCFrame(CFrame.new(-520.95, 51.9, -110.35));
elseif MediumNum == 3 then
	local MediumObby = RoundComponents.Objects.Medium["WHAT?! HOW?! - Medium"]:Clone();
	MediumObby.Parent = workspace;
	MediumObby:SetPrimaryPartCFrame(CFrame.new(-520.95, 51.9, -110.35));
end;

local HardNum = math.random(1, 3);
local HardObby = nil;

if HardNum == 1 then
	HardObby = RoundComponents.Objects.Hard["Guesser - Hard"]:Clone();
	HardObby.Parent = workspace;
	HardObby:SetPrimaryPartCFrame(CFrame.new(-577.55, 51.9, -110.35));
elseif HardNum == 2 then
	local HardObby = RoundComponents.Objects.Hard["Guesser 2.0 - Hard"]:Clone();
	HardObby.Parent = workspace;
	HardObby:SetPrimaryPartCFrame(CFrame.new(-577.55, 51.9, -110.35));
elseif HardNum == 3 then
	HardObby = RoundComponents.Objects.Hard["Extreme Lava Jumps - Hard"]:Clone();
	HardObby.Parent = workspace;
	HardObby:SetPrimaryPartCFrame(CFrame.new(-577.55, 51.9, -110.35));
end;

for i = 60, 0, -1 do
	if i > 1 then
		wait(1);
		RoundComponents.Status.Value = "In Game: "..i.." seconds until game is over!";
	elseif i == 1 then
		wait(1);
		RoundComponents.Value = "In Game: "..i.." second until game is over!";
	else
		wait(1);
		RoundComponents.Value = "Time's up!";
		wait(1);
		for i, v in pairs(Services.Players:GetPlayers()) do
			if v.PlayerInGame.Value == true then
				RoundFunctions.resetPlayer(v, true);
			end;
		end;
		if #RoundComponents.Participants > 0 then
			table.clear(RoundComponents.Participants);
		end;
		EasyObby:Destroy();
		MediumObby:Destroy();
		HardObby:Destroy();
	end;
end;
1 Like

Sorry for being offline for so long! I tried your connection system and it worked! I just have 2 things.

  1. How does this work? - I’m a very amateur scripter with tables.
  2. I don’t know why but my reset vote code just started acting up with my timer system. Would this have anything to do with it?
    Thanks for your help!

Hello! I just remembered something! When I created the two connections for player.AncestroyChanged and humanoid.Died, remember to wrap them in a table which contains the connections (we want to disconnect these for that player).

so it would look more like this:

connections[player.UserId.."Left"] = player.AncestryChanged:Connect(function()
    removePlayer(player)
end)
	
connections[player.UserId.."Died"] = character.Humanoid.Died:Connect(function()
	removePlayer(player)
end)

And change this:

local function removePlayer(player)
	local playerFound = table.find(activePlayers, player)
	if (playerFound) then
		table.remove(activePlayers, playerFound)
        connections[player.UserId.."Left"]:Disconnect()
        connections[player.UserId.."Died"]:Disconnect()
	end	
end

We want to store the connections so we can disconnect them, preventing them from being invoked when the player dies/leaves but they’re not even in the game (the minigame not the session) anymore. This is also useful to prevent memory leaks, having signals/connections that are no longer needed by the game but are still being computed which takes memory.
You can read about memory leaks here:
Garbage Collection and Memory Leaks in Roblox - What you should know

That’s why I included this:

-- Cleaning up connections
for _, connection in pairs(connections) do
	connection:Disconnect()
end

but I forgot about tagging the connections in the first place. Anyways.

So the way how this system works is it uses RBXScriptConnections to immediate response, looping for conditions has it’s downsides:

  • Reconnecting Signals for no reason.
  • Having to wait a second before it checks the conditions, the player would have to wait for a late response which could make or break a game in those clutch moments.

When the round begins we create two connections, one for when the player left and one for when the character dies. AncestryChanged() fires when the instance has been parented somewhere else, in this case when the player leaves they’re parented to nil. Humanoid.Died() is pretty self explanatory, invokes (invoking and firing mean the same thing. Meaning, calling something) when the player has died. When the player leaves or dies we want to remove them from the table which depicts other conditions, i.e the number of players left or winner results. The round condition for checking the number of players still has to wait a maximum of 1 second before it’s checked again.

As for the reset vote code, I’d need to look into that to understand what’s going on and the clashes they make

1 Like