Runden hinzufügen ermöglicht es Ihnen, das Gameplay in Phasen mit einem klaren Start- und Endpunkt zu strukturieren, sodass die Spieler ihren Fortschritt messen und regelmäßig die Möglichkeit haben, auf einem gleichen Spielfeld zu spielen. Dies ist besonders wichtig für team-basiertes Gameplay, da es den Spielern die Möglichkeit bietet, ihren Spielstil je nach Teamzusammensetzung während dieser Runde zu ändern.
Anhand des Beispiel-Laser-Tag-Spiels lehrt Sie dieser Abschnitt des Tutorials, wie Sie die integrierten Funktionen von Roblox nutzen und anpassen, um jede Runde zu strukturieren, einschließlich Skriptanleitungen zu:
- Beginn einer Runde durch Zurücksetzen der individuellen und Team-Punkte und anschließendes Spawnen der Spieler in ihre Team-Spawn-Zonen.
- Anpassen von Variablen, die das Ziel für die Runde oben auf dem Bildschirm jedes Spielers festlegen.
- Verfolgen der Punktbeiträge der Spieler für den 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 die neutrale Lobby gespawnt werden.
Nachdem Sie diesen Abschnitt abgeschlossen haben, werden Sie lernen, wie Sie das Verhalten von Blastern implementieren, das sowohl genau als auch befriedigend für die Spieler ist.

Startschleife
ServerScriptService ⟩ Gameplay ⟩ Rounds verarbeitet die meiste Logik zur Implementierung von Runden und beginnt, indem es die Funktion startRoundLoopAsync() aufruft, um den Beginn einer Runden-Schleife zu markieren. Während die Spieler in die Lobby eintreten und warten, um in ein Team sortiert zu werden, ruft startRoundLoopAsync() die Funktion resetScores() in ServerScriptService ⟩ Gameplay ⟩ Scoring auf, um sowohl die Rangliste als auch die Team-Punkte zurückzusetzen.
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
endDa jetzt alle mit null Punkten beginnen, setzt startRoundLoopAsync() dann die Neutral-Eigenschaft des Neutral-Spawnstandorts auf false, sodass nur Spieler mit der gleichen Team.Color-Eigenschaft wie die TeamColor-Eigenschaft des Spawnstandorts dort spawnen können. Da die TeamColor-Eigenschaft des Spawnstandorts 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 Spieler, die sich derzeit im Spiel befinden, 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 dem Spiel beitreten, nachdem die Lobby-Gruppe in ein Team sortiert wurde, hört startRoundLoopAsync() auf das Ereignis Players.PlayerAdded:Connect und ruft dann erneut die Funktion spawnPlayersInMap auf, um sie dem Team mit der geringsten Anzahl von Spielern hinzuzufügen. Weitere Informationen zu diesem Prozess finden Sie in Spawn-Standorte konfigurieren aus dem vorherigen Abschnitt Spawnen und Respawnen des Tutorials.
-- Alle Spieler auf der Karte spawnen
neutralSpawn.Neutral = false
spawnPlayersInMap(Players:GetPlayers())
-- Neue Spieler auf der Karte spawnen, 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 Spiel Anweisungen geben, was zu tun ist, um innerhalb der Runde erfolgreich zu sein. Das Beispiel-Laser-Tag-Spiel erfüllt diese Anforderung, indem es einen Zielprompt oben auf dem Bildschirm jedes Spielers bereitstellt, der klare Anweisungen gibt, was das Team tun muss, um zu gewinnen.

Während Sie mehr darüber erfahren können, wie Sie die Ziel-UI Komponente im UI-Curriculum konfigurieren und anzeigen, konzentriert sich dieser Abschnitt darauf, wie Sie das Ziel zu Beginn der Runde implementieren, beginnend mit der Festlegung der Anzahl der Punkte, die jedes Team benötigt, um die Runde abzuschließen.
Obwohl der Zielprompt zur Laufzeit die Spieler informiert, dass sie drei Punkte erzielen müssen, um zu gewinnen, können Sie, wenn Sie den Prompt in StarterGui ⟩ HUDGui untersuchen, sehen, dass er stattdessen einen konfigurierbaren "%d" für den Punktwert enthält.

"%d" ist eine Platzhalterzeichenfolge, die Sie jederzeit erhöhen oder verringern können, um Ihren eigenen Gameplay-Anforderungen gerecht zu werden, indem Sie die TEAM_SCORE_LIMIT-Variable in ReplicatedStorage ⟩ TEAM_SCORE_LIMIT aktualisieren. Wenn Sie diese Zahl beispielsweise auf einen übermäßig hohen Wert von 200 setzen, wird der Prompt und der Team-Punktzähler entsprechend aktualisiert.
local TEAM_SCORE_LIMIT = 200 -- aktualisierte Zeile, unbedingt zurückändern
return TEAM_SCORE_LIMIT
Dieses einfache Variablen-Update funktioniert zur Laufzeit, da beim Start der Runde ReplicatedStorage ⟩ HUDGuiSetup ⟩ SetObjective das TEAM_SCORE_LIMIT-Modulskript benötigt, damit es die Platzhalterzeichenfolge im TextLabel-Objekt der UI-Zielkomponente austauschen kann.
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)
endPunkte verfolgen
Jetzt, da die Spieler ein Ziel für die Runde haben, muss das Spiel die Punkte jedes Teams verfolgen, 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 ihrem Team-Score addiert, ist es wichtig, Punkte an einem separaten Ort für rundenbasiertes Gameplay zu speichern und zu überwachen, da, wenn ein Spieler punktet und das Spiel verlässt, bevor die Runde vorbei ist, sein Beitrag sofort von der Rangliste abgezogen wird, sobald er sich vom Spiel trennt.
Um sicherzustellen, dass dies nicht passiert und jeder Beitrag zum Teamziel erhalten bleibt, speichert ReplicatedStorage ⟩ HUDGuiSetup ⟩ StartSyncingTeamPoints alle Punkte separat unter dem teamPoints-Attribut im Teams-Dienst. Wenn teamPoints erhöht wird, ruft dieses Modulskript die Funktion startSyncingTeamPoints auf, um den Teamzähler Class.GuiObjects innerhalb der Ziel-UI-Komponente zu finden.
Wenn es TeamACounter und TeamBCounter findet, erhält es deren teamColor-Attribut, das mit den Team-Spawn-Zonen korreliert: TeamACounter zeigt die Punkte des grünen Teams an, und TeamBCounter verfolgt die Punkte des rosa Teams.
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 Modulskript ruft dann seine Funktion getTeamFromTeamColor auf, um zu validieren, dass das mint teamColor-Attribut des TeamACounters und das carnation pink teamColor-Attribut des TeamBCounters beide mit den entsprechenden Team.Color-Eigenschaften im Teams-Dienst übereinstimmen. Wenn ja, gibt es beide Teams zurück.
local function getTeamFromTeamColor(teamColor: Color3): Team?
for _, team in Teams:GetTeams() do
if team.TeamColor == teamColor then
return team
end
end
return nil
endWenn dies der Fall ist, setzt startSyncingTeamPoints die TextLabel-Objekte beider Teamzä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.
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 Punkte auf dem Bildschirm des Spielers verfolgt werden, aber es ist wichtig, die Logik zu überprüfen, die die Punkte auf dem Server verfolgt, damit er weiß, wann ein Team das Ziel erreicht und die Runde gewinnt. Wenn Sie ServerScriptService ⟩ Gameplay ⟩ Scoring erneut besuchen, können Sie sehen, dass das Modulskript beginnt, indem es ein bindbares Ereignis erstellt, das jedes Mal ausgelöst wird, wenn ein Spieler einen Punkt erzielt.
local teamScoreChangedBindable = Instance.new("BindableEvent")
local Scoring = {
teamScoreChanged = teamScoreChangedBindable.Event,
}Es ruft dann die Funktion incrementScore auf, die die folgenden Aktionen ausführt:
- Holt sich den individuellen Punktestand des Spielers auf der Rangliste und addiert eins.
- Löst das zuvor erwähnte bindbare Ereignis mit dem Team des Spielers und dessen Punktestand aus.
Dieser Prozess sorgt dafür, dass sowohl der Client als auch der Server in Bezug auf die individuellen Punktestände der Spieler und deren Team-Punkte synchronisiert bleiben.
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)
endErgebnisse anzeigen
Während die Spieler einander ausschalten und Punkte für ihr Team erzielen, überprüft ServerScriptService ⟩ Gameplay ⟩ Rounds, ob das Team, das punktete, das Ziel der Runde erreicht hat. Wenn der Team-Score unter der TEAM_SCORE_LIMIT-Variable in ReplicatedStorage ⟩ TEAM_SCORE_LIMIT liegt, wartet der Server weiter, bis eines der Teams erneut punktet.
Sobald jedoch der Punktestand eines Teams die TEAM_SCORE_LIMIT-Variable erreicht, löst das Skript eine roundWinnerRemote-Ereignisinstanz mit dem Namen des Spielers und seinem Team aus.
-- Überprüfen, ob die Runde nach jedem Punktestand beendet ist
local team: Team
local score: number = 0
while score < TEAM_SCORE_LIMIT do
team, score = Scoring.teamScoreChanged:Wait()
end
-- Gewonnenes Team anzeigen
for _, player in Players:GetPlayers() do
-- Senden, in welchem Team der Spieler am Ende der Runde ist
-- da das Team des Spielers gleich entfernt wird, sodass der Client
-- sein eigenes Team nicht überprüfen kann
roundWinnerRemote:FireClient(player, team, player.Team)
endDas Skript ReplicatedStorage ⟩ RoundResultsGuiSetup auf jedem Client hört auf diese roundWinnerRemote-Ereignisinstanz, damit es:
- Einen einzigartigen StarterGui ⟩ RoundResultsGui UI-Bildschirm anzeigen kann, der die Ergebnisse der Runde und ob der Spieler im gewinnenden Team war, ankündigt.
- Ein Audio-Clip für Sieg oder Niederlage abspielen kann.
Wenn ein Spieler beispielsweise im Team ist, das den entscheidenden Punkt erzielt hat, erhält er mehrere Formen von Feedback zu den Ergebnissen der Runde in Form eines UI-Bildschirms, der einen Siegtext anzeigt, und eines Audio-Clips, der einen freudigen Klang abspielt. Umgekehrt erhält ein Spieler, der nicht im Team ist, das den entscheidenden Punkt erzielt hat, einen UI-Bildschirm, der einen Niederlage-Text anzeigt, und einen Audio-Clip, der einen ominösen Klang abspielt.
local function onRoundWinner(winner: Team, localTeam: Team?)
local victoryDefeatText = "Runde beendet!"
if localTeam then
-- Wenn unser Team gewonnen hat, zeigen wir Sieg! Andernfalls Niederlage...
local isVictory = winner == localTeam
if isVictory then
victorySound:Play()
victoryDefeatText = VICTORY_TEXT
else
defeatSound:Play()
victoryDefeatText = DEFEAT_TEXT
end
endTeams zurücksetzen
Zur gleichen Zeit, in der ServerScriptService ⟩ Gameplay ⟩ Rounds überprüft, ob ein Team das Ziel der Runde erreicht 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 startet den Prozess des formellen Endes der Runde und des Zurücksetzens beider Teams.
Mit der gleichen Logik in Spawn-Standorte konfigurieren setzt Rounds dann die Neutral-Eigenschaft des Neutral-Spawnstandorts auf true, sodass Spieler dort spawnen können, unabhängig von ihrem Teamstatus. Das bedeutet, dass die Lobby der einzige Ort wird, an dem Spieler spawnen können, nachdem sie von der Runde getrennt wurden.
-- Alle in die Lobby senden
playerAddedConnection:Disconnect()
neutralSpawn.Neutral = true
spawnPlayersInLobby(Players:GetPlayers())Nachdem zehn Sekunden für eine Pause gewartet wurde, startet das Rounds-Server-Skript dann die Schleife erneut, indem es die Punktestände aller zurücksetzt und sie in neue Teams sortiert. Das Beispiel wiederholt diesen zyklischen Rundenprozess erneut, bis keine Spieler mehr im Server sind.
Jetzt, da die Spieler in die Karte mit ihrem eigenen Team spawnen und eine volle Runde spielen können, lehrt Sie der nächste Abschnitt die Skripte hinter dem Verhalten jedes Blasters.