Agregar rondas te permite estructurar la jugabilidad en fases con un inicio y un final claros para que los jugadores puedan medir su progreso y tener una oportunidad periódica de un campo de juego igualitario. Esto es particularmente importante para la jugabilidad en equipo, ya que ofrece a los jugadores la oportunidad de cambiar su estilo de juego según quién esté en su equipo durante esa ronda.
Utilizando la experiencia de láser tag de ejemplo como referencia, esta sección del tutorial te enseña cómo utilizar y personalizar las funciones integradas de Roblox para estructurar cada ronda, incluyendo orientación sobre scripting en:
- Comenzar una ronda restableciendo los puntos individuales y del equipo, y luego haciendo aparecer a los jugadores en sus zonas de aparición de equipo.
- Personalizar variables que establecen el objetivo de la ronda en la parte superior de la pantalla de cada jugador.
- Rastrear las contribuciones de puntos de los jugadores para la puntuación de su equipo.
- Activar pantallas UI únicas dependiendo de si el equipo del jugador ganó o perdió la ronda.
- Terminar una ronda desconectando a los jugadores y haciéndolos aparecer en el vestíbulo neutral.
Después de completar esta sección, aprenderás a implementar un comportamiento de blaster que sea preciso y satisfactorio para los jugadores.

Iniciar bucle
ServerScriptService ⟩ Gameplay ⟩ Rounds maneja la mayor parte de la lógica para implementar rondas y comienza llamando a la función startRoundLoopAsync() para marcar el inicio de un bucle de ronda. Mientras los jugadores se unen al vestíbulo y esperan para ordenarse en un equipo, startRoundLoopAsync() llama a la función resetScores() en ServerScriptService ⟩ Gameplay ⟩ Scoring para restablecer tanto la tabla de líderes como los puntos del equipo.
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
Ahora que todos comienzan con cero puntos, startRoundLoopAsync() establece la propiedad Neutral de la ubicación de aparición Neutral en false para que solo los jugadores con la misma propiedad Team.Color que la propiedad TeamColor de la ubicación de aparición puedan aparecer allí. Debido a que la propiedad TeamColor de la ubicación de aparición está configurada como blanco en lugar de los equipos de menta o rosa carníneo del ejemplo, esta configuración impide que todos los jugadores aparezcan o reaparezcan allí mientras una ronda está activa.
Para los jugadores que actualmente están en el vestíbulo, startRoundLoopAsync() pasa todos los jugadores dentro de la experiencia a la función spawnPlayersInMap en ServerScriptService ⟩ Gameplay ⟩ Rounds ⟩ spawnPlayersInMap para ordenar y equilibrar a todos en un equipo con aproximadamente el mismo número de jugadores.
Para cualquier nuevo jugador que se una a la experiencia después de que el grupo del vestíbulo se haya ordenado en un equipo, startRoundLoopAsync() escucha el evento Players.PlayerAdded:Connect, y luego llama a la función spawnPlayersInMap nuevamente para añadirlos al equipo con la menor cantidad de jugadores. Para obtener más información sobre este proceso, consulta Configurar ubicaciones de aparición de la sección anterior Apariciones y reapariciones del tutorial.
Rounds
-- Hacer aparecer a todos los jugadores en el mapa
neutralSpawn.Neutral = false
spawnPlayersInMap(Players:GetPlayers())
-- Hacer aparecer nuevos jugadores en el mapa cuando se unen
local playerAddedConnection = Players.PlayerAdded:Connect(function(player: Player)
spawnPlayersInMap({ player })
end)
Establecer objetivo
Ahora que cada jugador está en la arena con sus compañeros de equipo, la experiencia necesita proporcionar instrucciones sobre qué hacer para tener éxito dentro de la ronda. La experiencia de láser tag de ejemplo aborda este requisito proporcionando un aviso objetivo en la parte superior de la pantalla de cada jugador con una clara orientación sobre lo que el equipo necesita hacer para ganar.

Si bien puedes aprender más sobre cómo configurar y mostrar el componente UI de objetivo en el Curriculum de UI, esta sección se centra en cómo implementar la meta objetivo al comenzar la ronda, comenzando por cómo establecer la cantidad de puntos que cada equipo necesita para completar la ronda.
A pesar de que el aviso objetivo en tiempo de ejecución informa a los jugadores que necesitan anotar tres puntos para ganar, si examinas el aviso en StarterGui ⟩ HUDGui, puedes ver que contiene un "%d" configurable para el valor de los puntos.

"%d" es una cadena de marcador de posición que puedes aumentar o disminuir en cualquier momento para satisfacer tus propios requisitos de jugabilidad al actualizar la variable TEAM_SCORE_LIMIT en ReplicatedStorage ⟩ TEAM_SCORE_LIMIT. Por ejemplo, si estableces este número en un excesivamente alto 200, el aviso y el contador de puntos del equipo se actualizarían en consecuencia.
TEAM_SCORE_LIMITlocal TEAM_SCORE_LIMIT = 200 -- línea actualizada, asegúrate de cambiar de nuevoreturn TEAM_SCORE_LIMIT

Esta simple actualización de variable funciona en tiempo de ejecución porque, al comenzar la ronda, ReplicatedStorage ⟩ HUDGuiSetup ⟩ SetObjective requiere el script de módulo TEAM_SCORE_LIMIT para que pueda intercambiar la cadena de marcador de posición en el objeto TextLabel del UI de Objetivo.
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
Rastrear puntos
Ahora que los jugadores tienen un objetivo para la ronda, la experiencia necesita seguir la pista de los puntos de cada equipo hasta que cumplan su objetivo. Mientras el comportamiento predeterminado del servicio Teams agrupa automáticamente a cada jugador debajo de su equipo y suma las contribuciones de cada jugador a la puntuación de su equipo, es importante almacenar y monitorear los puntos en un lugar separado para la jugabilidad basada en rondas, porque si un jugador puntúa y luego se va antes de que la ronda haya terminado, su contribución se deduce de la tabla de líderes tan pronto como se desconecta de la experiencia.
Para asegurarte de que esto no suceda y todas las contribuciones hacia el objetivo del equipo se preserven, ReplicatedStorage ⟩ HUDGuiSetup ⟩ StartSyncingTeamPoints almacena todos los puntos por separado bajo el atributo teamPoints en el servicio Teams. A medida que teamPoints aumenta, este script de módulo llama a la función startSyncingTeamPoints para encontrar el contador de equipo Class.GuiObjects dentro del componente UI de Objetivo.
Cuando localiza TeamACounter y TeamBCounter, obtiene su atributo teamColor, que se correlaciona con las zonas de aparición del equipo: TeamACounter muestra los puntos del equipo verde, y TeamBCounter rastrea los puntos del equipo 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)
El script de módulo luego llama a su función getTeamFromTeamColor para validar que el atributo teamColor de TeamACounter menta y el atributo teamColor de TeamBCounter rosa carníneo coincidan con las propiedades Team.Color correspondientes en el servicio Teams. Si es así, devuelve ambos equipos.
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
Cuando esto ocurre, startSyncingTeamPoints establece los objetos TextLabel de ambos contadores de equipo a sus correspondientes valores de teamPoints, y continúa actualizándolos cada vez que un jugador anota un punto etiquetando a otro jugador del equipo contrario.
StartSyncingTeamPoints
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function()
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
Todo en esta sección hasta ahora se ha centrado en cómo rastrear los puntos en la pantalla del jugador, pero es importante revisar la lógica que maneja el rastreo de puntos en el servidor para que sepa cuándo un equipo cumple el objetivo y gana la ronda. Si vuelves a ServerScriptService ⟩ Gameplay ⟩ Scoring, puedes ver que el script de módulo comienza creando un evento unible que se activará cada vez que un jugador anote un punto.
Scoringlocal teamScoreChangedBindable = Instance.new("BindableEvent")local Scoring = {teamScoreChanged = teamScoreChangedBindable.Event,}
Luego llama a la función incrementScore, que realiza las siguientes acciones:
- Obtiene la puntuación individual del jugador en la tabla de líderes y suma uno.
- Activa el evento unible mencionado anteriormente con tanto el equipo del jugador como su puntuación.
Este proceso mantiene efectivamente tanto al cliente como al servidor alineados respecto a las puntuaciones individuales de los jugadores y sus puntuaciones de equipo.
Scoring
function Scoring.incrementScore(player: Player, amount: number)
local team = player.Team
assert(team, `El jugador {player.Name} debe estar en un equipo para anotar un punto, pero no tiene equipo`)
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
Mostrar resultados
A medida que los jugadores se etiquetan entre sí y anotan puntos para su equipo, ServerScriptService ⟩ Gameplay ⟩ Rounds verifica si el equipo que anotó cumple con el objetivo de la ronda. Si la puntuación de su equipo es menor que la variable TEAM_SCORE_LIMIT en ReplicatedStorage ⟩ TEAM_SCORE_LIMIT, el servidor continúa esperando hasta que uno de los equipos anote nuevamente.
Sin embargo, una vez que la puntuación de un equipo alcanza la variable TEAM_SCORE_LIMIT, el script activa una instancia de evento roundWinnerRemote con el nombre del jugador y su equipo.
Rounds-- Verificar si la ronda ha terminado después de cada anotaciónlocal team: Teamlocal score: number = 0while score < TEAM_SCORE_LIMIT doteam, score = Scoring.teamScoreChanged:Wait()end-- Mostrar equipo ganadorfor _, player in Players:GetPlayers() do-- Enviar qué equipo tiene el jugador al final de la ronda-- porque el equipo del jugador está a punto de ser eliminado, por lo que el cliente-- no podrá verificar su propio equiporoundWinnerRemote:FireClient(player, team, player.Team)end
El script ReplicatedStorage ⟩ RoundResultsGuiSetup en cada cliente escucha esta instancia de evento roundWinnerRemote para que pueda:
- Mostrar una pantalla UI única StarterGui ⟩ RoundResultsGui que anuncia los resultados de la ronda y si el jugador estaba en el equipo ganador.
- Reproducir un clip de audio de victoria o derrota.
Por ejemplo, si un jugador está en el equipo que anotó el punto ganador, recibe múltiples formas de retroalimentación sobre los resultados de la ronda en forma de una pantalla UI que muestra un texto de victoria, y un clip de audio que reproduce un sonido alegre. Por el contrario, si un jugador no está en el equipo que anotó el punto ganador, recibe una pantalla UI que muestra un texto de derrota, y un clip de audio que reproduce un sonido ominoso.
RoundResultsGuiSetup
local function onRoundWinner(winner: Team, localTeam: Team?)
local victoryDefeatText = "¡La ronda ha terminado!"
if localTeam then
-- Si nuestro equipo ganó, mostraremos ¡Victoria! De lo contrario, mostraremos Derrota...
local isVictory = winner == localTeam
if isVictory then
victorySound:Play()
victoryDefeatText = VICTORY_TEXT
else
defeatSound:Play()
victoryDefeatText = DEFEAT_TEXT
end
end
Restablecer equipos
Al mismo tiempo que ServerScriptService ⟩ Gameplay ⟩ Rounds verifica que un equipo cumpla con el objetivo de la ronda y activa la mostración de UI apropiada para cada jugador, también transporta a todos los jugadores de la arena al vestíbulo desconectándolos de la ronda. Esto inicia el proceso de finalizar formalmente la ronda y restablecer ambos equipos.
Utilizando la misma lógica en Configurar ubicaciones de aparición, Rounds luego establece la propiedad Neutral de la ubicación de aparición Neutral en true para que los jugadores puedan aparecer allí independientemente de su estado de equipo. Esto significa que el vestíbulo se convierte en el único lugar donde los jugadores pueden aparecer después de ser desconectados de la ronda.
Rounds-- Enviar a todos al vestíbuloplayerAddedConnection:Disconnect()neutralSpawn.Neutral = truespawnPlayersInLobby(Players:GetPlayers())
Después de esperar diez segundos por una interrupción, el script del servidor Rounds comienza el bucle nuevamente restableciendo las puntuaciones de todos y ordenándolos en nuevos equipos. El ejemplo repite este proceso cíclico de ronda nuevamente hasta que no haya jugadores en el servidor.
Ahora que los jugadores pueden aparecer en el mapa con su propio equipo y jugar una ronda completa, la siguiente sección te enseña sobre los scripts detrás del comportamiento de cada blaster.