Runden hinzufügen ermöglicht es, das Gameplay in Phasen mit einem klaren Anfang und Ende zu strukturieren, sodass die Spieler ihren Fortschritt messen und regelmäßig eine Chance auf einen gleichen Spielstand haben. Dies ist besonders wichtig für team-basierte Spiele, da es den Spielern ermöglicht, ihren Spielstil je nach Teamzusammensetzung in dieser Runde zu wechseln.
Anhand der Beispiel-Laser-Tag-Erfahrung lehrt dich dieser Abschnitt des Tutorials, wie du die integrierten Funktionen von Roblox nutzen und anpassen kannst, um jede Runde zu strukturieren, einschließlich Skriptanleitungen zu:
- Beginnen einer Runde, indem individuelle und Team-Punkte zurückgesetzt werden und die Spieler zu ihren Team-Spawnzonen gespawnt werden.
- Anpassen von Variablen, die das Ziel für die Runde oben auf dem Bildschirm jedes Spielers festlegen.
- Nachverfolgen der Punktbeiträge der Spieler für ihren Team-Score.
- Auslösen einzigartiger UI-Bildschirme, je nachdem, ob das Team des Spielers die Runde gewonnen oder verloren hat.
- Beenden einer Runde, indem die Spieler getrennt und in der neutralen Lobby gespawnt werden.
Nachdem du diesen Abschnitt abgeschlossen hast, wirst du lernen, wie du das Verhalten des Blasters implementierst, das sowohl genau als auch zufriedenstellend für die Spieler ist.

Startschleife
ServerScriptService ⟩ Gameplay ⟩ Rounds verarbeitet die meiste Logik für die Implementierung von Runden und beginnt, indem es die Funktion startRoundLoopAsync() aufruft, um den Beginn einer Rundenloop zu kennzeichnen. Während die Spieler in der Lobby beitreten und warten, um einem Team zugeordnet zu werden, ruft startRoundLoopAsync() die Funktion resetScores() in ServerScriptService ⟩ Gameplay ⟩ Scoring auf, um sowohl die Rangliste als auch die Team-Punkte zurückzusetzen.
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
Da nun alle bei null Punkten beginnen, setzt startRoundLoopAsync() dann die Neutral Spawn-Location Neutral-Eigenschaft auf false, damit nur Spieler mit der gleichen Team.Color-Eigenschaft wie die TeamColor-Eigenschaft des Spawnortes dort spawnen können. Da die TeamColor-Eigenschaft des Spawnortes auf weiß anstelle der mint- oder fleischfarbenen Teams des Beispiels eingestellt ist, verhindert diese Konfiguration, dass alle Spieler dort spawnen oder respawnen, während eine Runde aktiv ist.
Für Spieler, die sich derzeit in der Lobby befinden, übergibt startRoundLoopAsync() alle derzeit in der Erfahrung befindlichen Spieler an die Funktion spawnPlayersInMap in ServerScriptService ⟩ Gameplay ⟩ Rounds ⟩ spawnPlayersInMap, um alle in ein Team mit ungefähr der gleichen Anzahl von Spielern zu sortieren und auszubalancieren.
Für neue Spieler, die der Erfahrung nach der Sortierung der Lobbygruppe beitreten, hört startRoundLoopAsync() auf das Ereignis Players.PlayerAdded:Connect, um die Funktion spawnPlayersInMap erneut aufzurufen, um sie dem Team mit der geringsten Anzahl von Spielern hinzuzufügen. Für weitere Informationen zu diesem Prozess siehe Spawn-Standorte konfigurieren aus dem vorherigen Abschnitt Spawnen und Respawnen des Tutorials.
Rounds
-- Spawn alle Spieler auf der Karte
neutralSpawn.Neutral = false
spawnPlayersInMap(Players:GetPlayers())
-- Spawn neue Spieler auf der Karte, wenn sie beitreten
local playerAddedConnection = Players.PlayerAdded:Connect(function(player: Player)
spawnPlayersInMap({ player })
end)
Ziel festlegen
Jetzt, da jeder Spieler mit seinen Teamkollegen in der Arena ist, muss das Erlebnis Anweisungen geben, was zu tun ist, um innerhalb der Runde erfolgreich zu sein. Das Beispiel-Laser-Tag-Erlebnis geht auf diese Anforderung ein, indem es einen Zieleingabebildschirm oben auf dem Bildschirm jedes Spielers bereitstellt, mit klaren Anweisungen, was das Team tun muss, um zu gewinnen.

Während du mehr darüber lernen kannst, wie du die Objective UI Komponente im UI-Lehrplan konfigurierst und anzeigst, konzentriert sich dieser Abschnitt darauf, wie man das Ziel zu Beginn der Runde implementiert, beginnend mit der Festlegung der Anzahl der Punkte, die jedes Team benötigt, um die Runde abzuschließen.
Obwohl die Zieleingabe während der Laufzeit den Spielern mitteilt, dass sie drei Punkte erzielen müssen, um zu gewinnen, kannst du im StarterGui ⟩ HUDGui sehen, dass es stattdessen einen konfigurierbaren "%d" für den Punktwert enthält.

"%d" ist eine Platzhalterzeichenfolge, die du jederzeit erhöhen oder verringern kannst, um deine eigenen Gameplay-Anforderungen zu erfüllen, indem du die TEAM_SCORE_LIMIT-Variable in ReplicatedStorage ⟩ TEAM_SCORE_LIMIT aktualisierst. Wenn du diese Zahl beispielsweise auf zuvor übermäßig hohe 200 setzt, werden die Eingabeaufforderung und der Team-Punkte-Zähler entsprechend aktualisiert.
TEAM_SCORE_LIMITlocal TEAM_SCORE_LIMIT = 200 -- aktualisierte Zeile, stelle sicher, dass du sie zurückänderstreturn TEAM_SCORE_LIMIT

Diese einfache Variablenaktualisierung funktioniert zur Laufzeit, da beim Start der Runde ReplicatedStorage ⟩ HUDGuiSetup ⟩ SetObjective das TEAM_SCORE_LIMIT-Modulskript benötigt, damit es die Platzhalterzeichenfolge im UI-Ziel-TextLabel-Objekt austauschen kann.
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
Punkte nachverfolgen
Jetzt, wo die Spieler ein Ziel für die Runde haben, muss das Erlebnis die Punkte jedes Teams nachverfolgen, bis sie ihr Ziel erreichen. Während das Standardverhalten des Teams-Dienstes automatisch jeden Spieler unter seinem Team gruppiert und die Beiträge jedes Spielers zu seinem Team-Score summiert, ist es wichtig, Punkte an einem separaten Ort für rundenbasiertes Gameplay zu speichern und zu überwachen, da, falls ein Spieler punktet und bevor die Runde beendet ist, die Verbindung trennt, sein Beitrag sofort von der Rangliste abgezogen wird, sobald er die Erfahrung verlässt.
Um sicherzustellen, dass dies nicht passiert und jeder Beitrag zum Teamziel erhalten bleibt, speichert ReplicatedStorage ⟩ HUDGuiSetup ⟩ StartSyncingTeamPoints alle Punkte separat unter dem Attribut teamPoints im Teams-Dienst. Während teamPoints inkrementiert wird, ruft dieses Modulsystem die Funktion startSyncingTeamPoints auf, um den Teamzähler Class.GuiObjects innerhalb der Objective UI-Komponente zu finden.
Wenn es TeamACounter und TeamBCounter findet, erhält es deren teamColor-Attribut, das den Team-Spawnzonen entspricht: TeamACounter zeigt die Punkte des grünen Teams an, und TeamBCounter verfolgt die Punkte des pinken Teams.
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)
Das Modulsystem ruft dann seine getTeamFromTeamColor-Funktion auf, um zu validieren, dass die mint teamColor-Eigenschaft des TeamACounters und die carnation pink teamColor-Eigenschaft des TeamBCounters mit den entsprechenden Team.Color-Eigenschaften im Teams-Dienst übereinstimmen. Wenn dies der Fall ist, gibt es beide Teams zurück.
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
Wenn dies geschieht, setzt startSyncingTeamPoints die TextLabel-Objekte beider Team-Zähler auf ihre entsprechenden teamPoints-Werte und aktualisiert sie weiterhin, wann immer ein Spieler einen Punkt erzielt, indem er einen anderen Spieler des gegnerischen Teams ausschaltet.
StartSyncingTeamPoints
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function()
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
Alles in diesem Abschnitt hat sich bisher darauf konzentriert, wie man Punkte auf dem Bildschirm des Spielers nachverfolgt, aber es ist wichtig, die Logik zu überprüfen, die die Punkte auf dem Server nachverfolgt, damit er weiß, wann ein Team das Ziel erreicht und die Runde gewinnt. Wenn du das ServerScriptService ⟩ Gameplay ⟩ Scoring erneut besuchst, siehst du, dass das Modulsystem zuerst ein bindbares Ereignis erstellt, das jedes Mal ausgelöst wird, wenn ein Spieler einen Punkt erzielt.
Scoringlocal teamScoreChangedBindable = Instance.new("BindableEvent")local Scoring = {teamScoreChanged = teamScoreChangedBindable.Event,}
Dann ruft es die Funktion incrementScore auf, die folgende Aktionen ausführt:
- Es erfasst den individuellen Punktestand des Spielers auf der Rangliste und addiert eins.
- Sie löst das zuvor erwähnte bindbare Ereignis mit sowohl dem Team des Spielers als auch deren Punktestand aus.
Dieser Prozess hält effektiv sowohl den Client als auch den Server in Bezug auf die individuellen Punktestände der Spieler und deren Team-Punkte synchronisiert.
Scoring
function Scoring.incrementScore(player: Player, amount: number)
local team = player.Team
assert(team, `Spieler {player.Name} muss in einem Team sein, um einen Punkt zu erzielen, hat aber kein Team`)
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
Ergebnisse anzeigen
Während die Spieler sich gegenseitig ausschalten und Punkte für ihr Team erzielen, prüft ServerScriptService ⟩ Gameplay ⟩ Rounds, ob das Team, das einen Punkt erzielt hat, das Rundenziel erreicht hat. Wenn der Team-Score niedriger ist als die TEAM_SCORE_LIMIT-Variable in ReplicatedStorage ⟩ TEAM_SCORE_LIMIT, wartet der Server weiterhin, bis eines der Teams erneut punkten kann.
Sobald jedoch der Punktestand eines Teams die TEAM_SCORE_LIMIT-Variable erreicht, feuert das Skript eine roundWinnerRemote-Ereignisinstanz mit dem Namen des Spielers und dessen Team ab.
Rounds-- Überprüfen, ob die Runde nach jedem Punktestand zu Ende istlocal team: Teamlocal score: number = 0while score < TEAM_SCORE_LIMIT doteam, score = Scoring.teamScoreChanged:Wait()end-- Gewinner-Team anzeigenfor _, player in Players:GetPlayers() do-- Informationen darüber, welchem Team der Spieler am Ende der Runde angehört-- weil das Team des Spielers kurz entfernt wird und der Client-- In der Lage ist, sein eigenes Team nicht mehr zu überprüfenroundWinnerRemote:FireClient(player, team, player.Team)end
Das ReplicatedStorage ⟩ RoundResultsGuiSetup-Skript auf jedem Client hört auf dieses roundWinnerRemote-Ereignis, um:
- Ein einzigartiges StarterGui ⟩ RoundResultsGui UI-Bildschirm anzuzeigen, der die Ergebnisse der Runde und ob der Spieler im Gewinner-Team war, ankündigt.
- Ein Audio-Clip für Sieg oder Niederlage abzuspielen.
Wenn beispielsweise ein Spieler im Team ist, das den entscheidenden Punkt erzielt hat, erhält er mehrere Rückmeldungen zu den Rundenergebnissen in Form eines UI-Bildschirms, der Siegtext anzeigt, und einem Audio-Clip, der einen fröhlichen Klang spielt. Umgekehrt, wenn ein Spieler nicht im Team ist, das den entscheidenden Punkt erzielt hat, erhält er einen UI-Bildschirm, der Niederlagetext anzeigt, und einen Audio-Clip, der einen ominösen Klang abspielt.
RoundResultsGuiSetup
local function onRoundWinner(winner: Team, localTeam: Team?)
local victoryDefeatText = "Runde beendet!"
if localTeam then
-- Wenn unser Team gewonnen hat, zeigen wir Sieg an! Andernfalls Niederlage...
local isVictory = winner == localTeam
if isVictory then
victorySound:Play()
victoryDefeatText = VICTORY_TEXT
else
defeatSound:Play()
victoryDefeatText = DEFEAT_TEXT
end
end
Teams zurücksetzen
Gleichzeitig, während ServerScriptService ⟩ Gameplay ⟩ Rounds überprüft, ob ein Team das Rundenziel erfüllt hat und die entsprechende UI-Anzeige für jeden Spieler auslöst, transportiert es auch alle Spieler von der Arena in die Lobby, indem es sie von der Runde trennt. Dies beginnt den Prozess, die Runde offiziell zu beenden und beide Teams zurückzusetzen.
Mit derselben Logik in Configure Spawn Locations setzt Rounds dann die Neutral Spawn-Location Neutral-Eigenschaft auf true, sodass Spieler dort spawnen können, unabhängig von ihrem Teamstatus. Dies bedeutet, dass die Lobby der einzige Ort wird, an dem Spieler nach ihrer Trennung von der Runde spawnen können.
Rounds-- Alle in die Lobby schickenplayerAddedConnection:Disconnect()neutralSpawn.Neutral = truespawnPlayersInLobby(Players:GetPlayers())
Nachdem zehn Sekunden für eine Unterbrechung gewartet wurde, startet das Rounds-Server-Skript dann den Loop erneut, indem es die Punktzahlen aller zurücksetzt und sie in neue Teams sortiert. Das Beispiel wiederholt diesen zyklischen Rundenprozess, bis keine Spieler mehr im Server sind.
Jetzt, da Spieler in die Karte mit ihrem eigenen Team spawnen und eine volle Runde spielen können, lehrt dich der nächste Abschnitt etwas über die Skripte hinter dem Verhalten jedes Blasters.