Dodanie rund pozwala na strukturyzację rozgrywki w fazy z wyraźnym punktem startowym i zakończenia, dzięki czemu gracze mogą mierzyć swoje postępy oraz mieć okresową możliwość równego pola do gry. To jest szczególnie ważne w rozgrywkach zespołowych, ponieważ daje graczom szansę na zmianę stylu gry w zależności od tego, kto jest w ich zespole w danej rundzie.
Korzystając z przykładowego doświadczenia z laserowym tagiem jako odniesienia, ta część samouczka nauczy Cię, jak korzystać z wbudowanych funkcji Roblox i dostosować je do strukturyzacji każdej rundy, w tym wskazówki dotyczące skryptowania dotyczące:
- Rozpoczęcia rundy poprzez resetowanie punktów indywidualnych i zespołowych, a następnie pojawianie się graczy w strefach spawnowych swoich zespołów.
- Dostosowywania zmiennych, które ustalają cel rundy na górze ekranu każdego gracza.
- Śledzenia wkładów punktowych graczy do wyniku ich zespołu.
- Wywoływania unikalnych ekranów UI w zależności od tego, czy zespół gracza wygrał lub przegrał rundę.
- Zakończenia rundy poprzez odłączenie graczy i spawnowanie ich w neutralnym lobby.
Po ukończeniu tej sekcji dowiesz się, jak zaimplementować zachowanie blasterów, które będzie zarówno dokładne, jak i satysfakcjonujące dla graczy.

Rozpocznij pętlę
ServerScriptService ⟩ Gameplay ⟩ Rounds obsługuje większość logiki związanej z implementacją rund, a zaczyna się od wywołania funkcji startRoundLoopAsync(), aby zaznaczyć początek pętli rund. Gdy gracze dołączają do lobby i czekają na przydzielenie do zespołu, startRoundLoopAsync() wywołuje funkcję resetScores() w ServerScriptService ⟩ Gameplay ⟩ Scoring, aby zresetować zarówno tablicę wyników, jak i punkty zespołów.
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
Teraz, gdy wszyscy zaczynają z zerową ilością punktów, startRoundLoopAsync() ustawia właściwość Neutral lokalizacji spawnowania na Neutral na false, aby tylko gracze z tym samym Team.Color mogli tam się pojawiać. Ponieważ właściwość TeamColor lokalizacji spawnowania jest ustawiona na biały, a nie na próbne zespoły miętowe lub różowe, ta konfiguracja zapobiega wszystkim graczom spawnowaniu lub respawnowaniu tam w trakcie aktywnej rundy.
Dla graczy, którzy obecnie znajdują się w lobby, startRoundLoopAsync() przekazuje wszystkich graczy znajdujących się w doświadczeniu do funkcji spawnPlayersInMap w ServerScriptService ⟩ Gameplay ⟩ Rounds ⟩ spawnPlayersInMap, aby posortować i zrównoważyć wszystkich w zespole z mniej więcej taką samą liczbą graczy.
Dla nowo dołączających graczy, którzy dołączą do doświadczenia po posortowaniu grupy lobbystycznej do zespołu, startRoundLoopAsync() nasłuchuje zdarzenia Players.PlayerAdded:Connect, a następnie znów wywołuje funkcję spawnPlayersInMap, aby dodać ich do zespołu z najmniejszą ilością graczy. Aby uzyskać więcej informacji na ten temat, zobacz Skonfiguruj lokalizacje spawn z poprzedniej sekcji Spawnowanie i Respawnowanie tego samouczka.
Rounds
-- Spawn all players in the map
neutralSpawn.Neutral = false
spawnPlayersInMap(Players:GetPlayers())
-- Spawn new players in the map when they join
local playerAddedConnection = Players.PlayerAdded:Connect(function(player: Player)
spawnPlayersInMap({ player })
end)
Ustal cel
Teraz, gdy każdy gracz jest w arenie ze swoimi towarzyszami, doświadczenie musi dostarczyć instrukcji, co należy zrobić, aby odnieść sukces w rundzie. Przykładowe doświadczenie z laserowym tagiem radzi sobie z tym wymaganiem, dostarczając informację o celu na górze ekranu każdego gracza z wyraźnymi wskazówkami, co zespół musi zrobić, aby wygrać.

Chociaż możesz dowiedzieć się więcej o tym, jak skonfigurować i wyświetlić komponent UI celu w Curriculum UI, ta sekcja koncentruje się na tym, jak zaimplementować cel, gdy runda się rozpoczyna, zaczynając od ustalenia, jaką ilość punktów każdy zespół musi osiągnąć, aby zakończyć rundę.
Nawet jeśli informacja o celu w czasie rzeczywistym informuje graczy, że muszą zdobyć trzy punkty, aby wygrać, jeśli sprawdzisz podpowiedź w StarterGui ⟩ HUDGui, możesz zobaczyć, że zamiast tego zawiera konfigurowalny "%d" dla wartości punktowej.

"%d" jest ciągiem zastępczym, który możesz zwiększać lub zmniejszać w dowolnym momencie, aby spełnić własne wymagania dotyczące rozgrywki, aktualizując zmienną TEAM_SCORE_LIMIT w ReplicatedStorage ⟩ TEAM_SCORE_LIMIT. Na przykład, jeśli ustawiłbyś tę liczbę na nadmiernie wysoką 200, zapewnienie i licznik punktów zespołu zaktualizowałby się odpowiednio.
TEAM_SCORE_LIMITlocal TEAM_SCORE_LIMIT = 200 -- zaktualizowana linia, upewnij się, że wrócisz do wcześniejszejreturn TEAM_SCORE_LIMIT

Ta prosta aktualizacja zmiennej działa w czasie rzeczywistym, ponieważ gdy runda się zaczyna, ReplicatedStorage ⟩ HUDGuiSetup ⟩ SetObjective wymaga modułowego skryptu TEAM_SCORE_LIMIT, aby mógł wymienić ciąg zastępczy w obiekcie TextLabel UI celu.
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
Śledź punkty
Teraz, gdy gracze mają cel na rundę, doświadczenie musi śledzić punkty każdego zespołu, aż osiągną swój cel. Podczas gdy domyślne zachowanie usługi Teams automatycznie grupuje każdego gracza pod ich zespołem i sumuje wkłady każdego gracza do wyniku zespołu, ważne jest, aby przechowywać i monitorować punkty w oddzielnej lokalizacji dla rozgrywki opartej na rundzie, ponieważ jeśli gracz zdobędzie punkt i opuści przed zakończeniem rundy, ich wkład zostanie odjęty od tablicy wyników z chwilą, gdy się odłączy.
Aby upewnić się, że to się nie zdarzy i każdy wkład w cel zespołu jest zachowany, ReplicatedStorage ⟩ HUDGuiSetup ⟩ StartSyncingTeamPoints przechowuje wszystkie punkty osobno pod atrybutem teamPoints w usłudze Teams. Gdy teamPoints zwiększa się, ten moduł wywołuje funkcję startSyncingTeamPoints, aby znaleźć licznik zespołu Class.GuiObjects w komponencie UI celu.
Gdy zlokalizuje TeamACounter i TeamBCounter, uzyskuje ich atrybut teamColor, który odpowiada strefom spawnu zespołów: TeamACounter wyświetla punkty zespołu zielonego, a TeamBCounter śledzi punkty zespołu różowego.
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)
Moduł skryptu następnie wywołuje funkcję getTeamFromTeamColor, aby zweryfikować, że atrybut teamColor miętowy licznika zespołu A oraz atrybut teamColor różowy licznika zespołu B odpowiadają odpowiadającym właściwościom Team.Color w usłudze Teams. Jeśli tak, zwraca oba zespoły.
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
Kiedy to nastąpi, startSyncingTeamPoints ustawia oba liczniki zespołów TextLabel na odpowiadające wartości teamPoints i kontynuuje ich aktualizację, gdy gracz zdobije punkt, eliminując innego gracza z przeciwnego zespołu.
StartSyncingTeamPoints
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
team:GetAttributeChangedSignal(GuiAttribute.teamPoints):Connect(function()
teamPointCounter.TextLabel.Text = team:GetAttribute(GuiAttribute.teamPoints)
Wszystko w tej sekcji do tej pory skupiało się na śledzeniu punktów na ekranie gracza, ale ważne jest, aby przejrzeć logikę, która obsługuje śledzenie punktów na serwerze, aby wiedział, kiedy zespół osiąga cel i wygrywa rundę. Jeśli wrócisz do ServerScriptService ⟩ Gameplay ⟩ Scoring, zobaczysz, że moduł skryptu zaczyna od utworzenia zdarzenia wiążącego, które zostanie uruchomione za każdym razem, gdy gracz zdobędzie punkt.
Scoringlocal teamScoreChangedBindable = Instance.new("BindableEvent")local Scoring = {teamScoreChanged = teamScoreChangedBindable.Event,}
Następnie wywołuje funkcję incrementScore, która wykonuje następujące czynności:
- Pobiera indywidualny wynik gracza na tablicy wyników i zwiększa o jeden.
- Uruchamia wcześniej wspomniane zdarzenie wiążące z zespołem gracza i jego wynikiem.
Ten proces skutecznie utrzymuje zgodność między klientem a serwerem zarówno odnośnie indywidualnych punktów graczy, jak i punktów ich zespołów.
Scoring
function Scoring.incrementScore(player: Player, amount: number)
local team = player.Team
assert(team, `Gracz {player.Name} musi być w zespole, aby zdobyć punkt, ale nie ma zespołu`)
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
Wyświetl wyniki
W miarę jak gracze eliminują się nawzajem i zdobywają punkty dla swojego zespołu, ServerScriptService ⟩ Gameplay ⟩ Rounds sprawdza, czy zespół, który zdobył punkty, osiągnął cel rundy. Jeśli ich wynik zespołu jest niższy niż zmienna TEAM_SCORE_LIMIT w ReplicatedStorage ⟩ TEAM_SCORE_LIMIT, serwer czeka, aż jeden z zespołów zdobędzie punkty ponownie.
Jednak gdy wynik zespołu osiągnie zmienną TEAM_SCORE_LIMIT, skrypt uruchamia instancję zdarzenia roundWinnerRemote z nazwą gracza i jego zespołem.
Rounds-- Sprawdź, czy runda skończyła się po każdym zdobyciu punktulocal team: Teamlocal score: number = 0while score < TEAM_SCORE_LIMIT doteam, score = Scoring.teamScoreChanged:Wait()end-- Wyświetl zwycięski zespółfor _, player in Players:GetPlayers() do-- Wysyłanie informacji o tym, do jakiego zespołu należy gracz na koniec rundy,-- ponieważ zespół gracza ma być usunięty, więc klient-- nie będzie mógł sprawdzić swojego zespołuroundWinnerRemote:FireClient(player, team, player.Team)end
Skrypt ReplicatedStorage ⟩ RoundResultsGuiSetup na każdym kliencie nasłuchuje tego zdarzenia roundWinnerRemote, aby móc:
- Wyświetlić unikalny interfejs UI StarterGui ⟩ RoundResultsGui, który ogłasza wyniki rundy i informuje, czy gracz był w zwycięskim zespole.
- Odtworzyć dźwięk zwycięstwa lub porażki.
Na przykład, jeśli gracz jest w zespole, który zdobył zwycięski punkt, otrzymuje wiele form informacji zwrotnej na temat wyników rundy w formie ekranu UI wyświetlającego tekst zwycięstwa oraz odtwarzanego dźwięku radosnego. W przeciwnym razie, jeśli gracz nie jest w zespole, który zdobył zwycięski punkt, otrzymuje ekran UI wyświetlający tekst porażki oraz odtwarzany dźwięk złowieszczy.
RoundResultsGuiSetup
local function onRoundWinner(winner: Team, localTeam: Team?)
local victoryDefeatText = "Runda zakończona!"
if localTeam then
-- Jeśli nasz zespół wygrał, wyświetlimy Zwycięstwo! W przeciwnym razie wyświetlimy Porażkę...
local isVictory = winner == localTeam
if isVictory then
victorySound:Play()
victoryDefeatText = VICTORY_TEXT
else
defeatSound:Play()
victoryDefeatText = DEFEAT_TEXT
end
end
Zresetuj zespoły
W tym samym czasie, gdy ServerScriptService ⟩ Gameplay ⟩ Rounds weryfikuje, że zespół spełnił cel rundy i aktywuje odpowiednie wyświetlanie UI dla każdego gracza, przenosi również wszystkich graczy z areny do lobby, odłączając ich od rundy. To rozpoczyna proces formalnego zakończenia rundy i resetowania obu zespołów.
Używając tej samej logiki w Skonfiguruj lokalizacje spawn, Rounds ustawia właściwość Neutral lokalizacji spawnowania na true, aby gracze mogli tam spać niezależnie od swojego statusu zespołu. Oznacza to, że lobby staje się jedynym miejscem, w którym gracze mogą się pojawiać po odłączeniu od rundy.
Rounds-- Wyślij wszystkich do lobbyplayerAddedConnection:Disconnect()neutralSpawn.Neutral = truespawnPlayersInLobby(Players:GetPlayers())
Po odczekaniu dziesięciu sekund na przerwę, skrypt serwera Rounds zaczyna cykl od nowa, resetując punkty wszystkich i przypisując ich do nowych zespołów. Przykład powtarza ten cykliczny proces rund, aż nie będzie już graczy w serwerze.
Teraz, gdy gracze mogą pojawiać się na mapie ze swoim zespołem i grać pełną rundę, w następnej sekcji nauczysz się o skryptach odpowiedzialnych za zachowanie każdego blastera.