Adicionar rodadas permite estruturar o gameplay em fases com um claro ponto de início e de término, para que os jogadores possam medir seu progresso e ter uma oportunidade periódica de um campo de jogo igualitário. Isso é especialmente importante para gameplay em equipe, pois oferece aos jogadores a chance de mudar seu estilo de jogo, dependendo de quem está em sua equipe durante aquela rodada.
Usando a experiência de laser tag de exemplo como referência, esta seção do tutorial ensina como usar e personalizar os recursos embutidos da Roblox para estruturar cada rodada, incluindo orientações de script sobre:
- Iniciar uma rodada reiniciando os pontos individuais e da equipe, em seguida, fazendo com que os jogadores apareçam em suas zonas de spawn da equipe.
- Personalizar variáveis que definem o objetivo da rodada na parte superior da tela de cada jogador.
- Acompanhar as contribuições de pontos dos jogadores para a pontuação da equipe.
- Acionar telas de interface do usuário exclusivas, dependendo se a equipe do jogador ganhou ou perdeu a rodada.
- Encerrar uma rodada desconectando os jogadores e reinserindo-os no lobby neutro.
Após concluir esta seção, você aprenderá a implementar o comportamento de blasters que é tanto preciso quanto satisfatório para os jogadores.

Iniciar loop
ServerScriptService ⟩ Gameplay ⟩ Rounds lida com a maior parte da lógica para implementar rodadas e começa chamando a função startRoundLoopAsync() para marcar o início de um loop de rodadas. Enquanto os jogadores entram no lobby e aguardam serem organizados em uma equipe, startRoundLoopAsync() chama a função resetScores() em ServerScriptService ⟩ Gameplay ⟩ Scoring para reiniciar tanto o placar quanto os pontos da equipe.
Scoring
function Scoring.resetScores()
for _, player in Players:GetPlayers() do
player.leaderstats.Points.Value = 0
end
for _, team in Teams:GetTeams() do
team:SetAttribute(GuiAttribute.teamPoints, 0)
end
end
Agora que todos estão começando com zero pontos, startRoundLoopAsync() então define a propriedade Neutral da localização de spawn Neutro como false, para que apenas jogadores com a mesma propriedade Team.Color da propriedade TeamColor da localização de spawn possam aparecer lá. Como a propriedade TeamColor da localização de spawn está definida como branca em vez das equipes de menta ou rosa carnê do exemplo, essa configuração impede que todos os jogadores apareçam ou reapareçam lá enquanto uma rodada está ativa.
Para jogadores que estão atualmente no lobby, startRoundLoopAsync() passa todos os jogadores atualmente dentro da experiência para a função spawnPlayersInMap em ServerScriptService ⟩ Gameplay ⟩ Rounds ⟩ spawnPlayersInMap para organizar e equilibrar todos em uma equipe com aproximadamente o mesmo número de jogadores.
Para quaisquer novos jogadores que entrarem na experiência após o grupo do lobby ser organizado em uma equipe, startRoundLoopAsync() escuta o evento Players.PlayerAdded:Connect, e chama a função spawnPlayersInMap novamente para adicioná-los à equipe com o menor número de jogadores. Para mais informações sobre este processo, veja Configurar locais de spawn da seção anterior Spawn e Respawn do tutorial.
Rounds
-- Apanhar todos os jogadores no mapa
neutralSpawn.Neutral = false
spawnPlayersInMap(Players:GetPlayers())
-- Apanhar novos jogadores no mapa quando eles entram
local playerAddedConnection = Players.PlayerAdded:Connect(function(player: Player)
spawnPlayersInMap({ player })
end)
Definir objetivo
Agora que cada jogador está na arena com seus companheiros de equipe, a experiência precisa fornecer instruções sobre o que fazer para ter sucesso na rodada. A experiência de laser tag de exemplo atende a essa necessidade fornecendo um aviso de objetivo na parte superior da tela de cada jogador, com orientações claras sobre o que a equipe precisa fazer para vencer.

Embora você possa aprender mais sobre como configurar e exibir o componente UI de Objetivo no Currículo de UI, esta seção se concentra em como implementar o objetivo assim que a rodada começa, começando por como definir a quantidade de pontos que cada equipe precisa para completar a rodada.
Mesmo que o aviso de objetivo em tempo de execução informe os jogadores de que eles precisam marcar três pontos para vencer, se você examinar o aviso em StarterGui ⟩ HUDGui, pode ver que ele contém um "%d" configurável para o valor dos pontos.

"%d" é uma string de espaço reservado que você pode aumentar ou diminuir a qualquer momento para atender aos seus próprios requisitos de gameplay, atualizando a variável TEAM_SCORE_LIMIT em ReplicatedStorage ⟩ TEAM_SCORE_LIMIT. Por exemplo, se você definir esse número como um excessivamente alto 200, o aviso e o contador de pontos da equipe seriam atualizados de acordo.
TEAM_SCORE_LIMITlocal TEAM_SCORE_LIMIT = 200 -- linha atualizada, certifique-se de mudar de voltareturn TEAM_SCORE_LIMIT

Essa simples atualização de variável funciona em tempo de execução porque, assim que a rodada começa, ReplicatedStorage ⟩ HUDGuiSetup ⟩ SetObjective requer o módulo de script TEAM_SCORE_LIMIT para que possa trocar a string de espaço reservado no TextLabel do objetivo da UI.
TEAM_SCORE_LIMIT
local TEAM_SCORE_LIMIT = require(ReplicatedStorage.TEAM_SCORE_LIMIT)
local function setObjective(gui: ScreenGui)
local bodyTextLabel = gui.Objective.ObjectiveDisplay.Body.BodyTextLabel
bodyTextLabel.Text = bodyTextLabel.Text:format(TEAM_SCORE_LIMIT)
end
Acompanhar pontos
Agora que os jogadores têm um objetivo para a rodada, a experiência precisa acompanhar os pontos de cada equipe até que elas atinjam seu objetivo. Enquanto o comportamento padrão do serviço Teams agrupa automaticamente cada jogador sob sua equipe e soma as contribuições de cada jogador para a pontuação da equipe, é importante armazenar e monitorar os pontos em um local separado para o gameplay baseado em rodadas, pois se um jogador marcar pontos e sair antes que a rodada termine, sua contribuição será descontada do placar assim que ele se desconectar da experiência.
Para garantir que isso não aconteça e que todas as contribuições em direção ao objetivo da equipe sejam preservadas, ReplicatedStorage ⟩ HUDGuiSetup ⟩ StartSyncingTeamPoints armazena todos os pontos separadamente sob o atributo teamPoints no serviço Teams. À medida que teamPoints é incremental, este módulo de script chama a função startSyncingTeamPoints para encontrar o contador da equipe Class.GuiObjects dentro do componente de UI do Objetivo.
Quando localiza TeamACounter e TeamBCounter, obtém seu atributo teamColor, que se correlaciona com as zonas de spawn da equipe: TeamACounter exibe os pontos da equipe verde, e TeamBCounter acompanha os pontos da equipe rosa.
StartSyncingTeamPoints
local function startSyncingTeamPoints(gui: ScreenGui)
for _, teamPointCounter in gui.Objective.TeamPointCounter:GetChildren() do
if not teamPointCounter:IsA("GuiObject") then
continue
end
local iconTeamColor = teamPointCounter:GetAttribute(GuiAttribute.teamColor)
O módulo de script então chama sua função getTeamFromTeamColor para validar que o atributo teamColor mint do TeamACounter e o atributo teamColor carnation pink do TeamBCounter correspondem às propriedades Team.Color correspondentes sob o serviço Teams. Se sim, ele retorna ambas as equipes.
StartSyncingTeamPoints
local function getTeamFromTeamColor(teamColor: Color3): Team?
for _, team in Teams:GetTeams() do
if team.TeamColor == teamColor then
return team
end
end
return nil
end
Quando isso ocorre, startSyncingTeamPoints define os objetos TextLabel de ambos os contadores de equipe para seus valores correspondentes de teamPoints e continua a atualizá-los sempre que um jogador marca um ponto ao eliminar outro jogador da equipe oposta.
StartSyncingTeamPoints
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function()
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
Tudo nesta seção até agora tem se concentrado em como rastrear pontos na tela do jogador, mas é importante revisar a lógica que lida com o rastreamento de pontos no servidor, para que ele saiba quando uma equipe atinge o objetivo e vence a rodada. Se você revisar ServerScriptService ⟩ Gameplay ⟩ Scoring, pode ver que o módulo de script começa criando um evento vinculável, que será acionado cada vez que um jogador marcar um ponto.
Scoringlocal teamScoreChangedBindable = Instance.new("BindableEvent")local Scoring = {teamScoreChanged = teamScoreChangedBindable.Event,}
Em seguida, ele chama a função incrementScore, que realiza as seguintes ações:
- Obtém a pontuação individual do jogador no placar e adiciona um.
- Aciona o evento vinculável mencionado anteriormente com tanto a equipe do jogador quanto sua pontuação.
Esse processo mantém efetivamente o cliente e o servidor alinhados em relação às pontuações individuais dos jogadores e suas pontuações de equipe.
Scoring
function Scoring.incrementScore(player: Player, amount: number)
local team = player.Team
assert(team, `O Jogador {player.Name} deve estar em uma equipe para marcar um ponto, mas não tem equipe`)
local teamPoints = team:GetAttribute(GuiAttribute.teamPoints)
teamPoints += amount
team:SetAttribute(GuiAttribute.teamPoints, teamPoints)
local leaderstat = player.leaderstats.Points
leaderstat.Value += amount
teamScoreChangedBindable:Fire(team, teamPoints)
end
Exibir resultados
À medida que os jogadores eliminam uns aos outros e marcam pontos para sua equipe, ServerScriptService ⟩ Gameplay ⟩ Rounds verifica se a equipe que marcou atingiu o objetivo da rodada. Se a pontuação da equipe for inferior à variável TEAM_SCORE_LIMIT em ReplicatedStorage ⟩ TEAM_SCORE_LIMIT, o servidor continua a esperar até que uma das equipes marque novamente.
No entanto, assim que a pontuação de uma equipe atinge a variável TEAM_SCORE_LIMIT, o script ativa uma instância do evento roundWinnerRemote com o nome do jogador e sua equipe.
Rounds-- Verificar se a rodada terminou após cada pontuaçãolocal team: Teamlocal score: number = 0while score < TEAM_SCORE_LIMIT doteam, score = Scoring.teamScoreChanged:Wait()end-- Exibir a equipe vencedorafor _, player in Players:GetPlayers() do-- Enviando para qual equipe o jogador pertence no final da rodada-- porque a equipe do jogador está prestes a ser removida, então o cliente-- não poderá verificar sua própria equiperoundWinnerRemote:FireClient(player, team, player.Team)end
O script ReplicatedStorage ⟩ RoundResultsGuiSetup em cada cliente escuta esta instância do evento roundWinnerRemote para que possa:
- Exibir uma tela de UI StarterGui ⟩ RoundResultsGui única que anuncia os resultados da rodada e se o jogador estava na equipe vencedora.
- Tocar um clipe de áudio de vitória ou derrota.
Por exemplo, se um jogador estiver na equipe que marcou o ponto da vitória, ele recebe múltiplas formas de feedback sobre os resultados da rodada na forma de uma tela de UI que exibe texto de vitória, e um clipe de áudio que toca um som alegre. Por outro lado, se um jogador não estiver na equipe que marcou o ponto da vitória, ele recebe uma tela de UI que exibe texto de derrota, e um clipe de áudio que toca um som ominoso.
RoundResultsGuiSetup
local function onRoundWinner(winner: Team, localTeam: Team?)
local victoryDefeatText = "Rodada encerrada!"
if localTeam then
-- Se nossa equipe vencer, exibiremos Vitória! Caso contrário, exibiremos Derrota...
local isVictory = winner == localTeam
if isVictory then
victorySound:Play()
victoryDefeatText = VICTORY_TEXT
else
defeatSound:Play()
victoryDefeatText = DEFEAT_TEXT
end
end
Redefinir equipes
Ao mesmo tempo que ServerScriptService ⟩ Gameplay ⟩ Rounds verifica se uma equipe atingiu o objetivo da rodada e aciona a exibição de UI apropriada para cada jogador, ele também transporta todos os jogadores da arena para o lobby desconectando-os da rodada. Isso inicia o processo de formalmente encerrar a rodada e redefinir ambas as equipes.
Usando a mesma lógica em Configurar locais de spawn, Rounds então define a propriedade Neutral da localização de spawn Neutro como true para que os jogadores possam aparecer lá, independentemente de seu status na equipe. Isso significa que o lobby se torna o único local onde os jogadores podem spawnar após serem desconectados da rodada.
Rounds-- Enviar todos para o lobbyplayerAddedConnection:Disconnect()neutralSpawn.Neutral = truespawnPlayersInLobby(Players:GetPlayers())
Após esperar dez segundos por uma interrupção, o script do servidor Rounds então inicia o loop novamente, redefinindo os pontos de todos e organizando-os em novas equipes. O exemplo repete esse processo cíclico de rodadas até que não haja mais jogadores no servidor.
Agora que os jogadores podem aparecer no mapa com sua própria equipe e jogar uma rodada completa, a próxima seção ensina sobre os scripts por trás do comportamento de cada blaster.