Dodawanie rund pozwala na strukturyzację rozgrywki w fazy z wyraźnym punktem startowym i końcowym, aby gracze mogli mierzyć swoje postępy i mieć okresową możliwość równej rywalizacji. Jest to szczególnie ważne w rozgrywkach drużynowych, ponieważ daje graczom szansę na zmianę stylu gry w zależności od tego, kto jest w ich drużynie w danej rundzie.
Korzystając z przykładowej gry w laser tag jako odniesienia, ta część samouczka nauczy cię, jak używać i dostosowywać wbudowane funkcje Roblox do strukturyzacji każdej rundy, w tym wskazówki dotyczące skryptów na temat:
- Rozpoczynania rundy przez resetowanie punktów indywidualnych i drużynowych, a następnie spawnowania graczy w ich strefach spawnowania drużyn.
- Dostosowywania zmiennych, które ustalają cel rundy na górze ekranu każdego gracza.
- Śledzenia wkładów punktowych graczy w wynik drużyny.
- Wywoływania unikalnych ekranów UI w zależności od tego, czy drużyna gracza wygrała, czy przegrała rundę.
- Kończenia rundy przez odłączenie graczy i spawnowanie ich w neutralnym lobby.
Po ukończeniu tej sekcji nauczysz się, jak wdrożyć zachowanie blasterów, które jest zarówno dokładne, jak i satysfakcjonujące dla graczy.

Rozpocznij pętlę
ServerScriptService ⟩ Gameplay ⟩ Rounds obsługuje większość logiki wdrażania rund i zaczyna od wywołania funkcji startRoundLoopAsync(), aby oznaczyć początek pętli rundy. Gdy gracze dołączają do lobby i czekają na przydzielenie do drużyny, startRoundLoopAsync() wywołuje funkcję resetScores() w ServerScriptService ⟩ Gameplay ⟩ Scoring, aby zresetować zarówno tablicę wyników, jak i punkty drużynowe.
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
endTeraz, gdy wszyscy zaczynają z zerowymi punktami, startRoundLoopAsync() ustawia właściwość Neutral lokalizacji spawnowania Neutral na false, aby tylko gracze z tą samą właściwością Team.Color co właściwość TeamColor lokalizacji spawnowania mogli się tam spawnować. Ponieważ właściwość TeamColor lokalizacji spawnowania jest ustawiona na biały zamiast drużyn mint lub carnation pink z przykładu, ta konfiguracja uniemożliwia wszystkim graczom spawnowanie lub respawnowanie się tam, gdy runda jest aktywna.
Dla graczy, którzy obecnie znajdują się w lobby, startRoundLoopAsync() przekazuje wszystkich graczy aktualnie w grze do funkcji spawnPlayersInMap w ServerScriptService ⟩ Gameplay ⟩ Rounds ⟩ spawnPlayersInMap, aby posortować i zrównoważyć wszystkich w drużynę z mniej więcej taką samą liczbą graczy.
Dla nowych graczy, którzy dołączają do gry po tym, jak grupa w lobby została przydzielona do drużyny, startRoundLoopAsync() nasłuchuje zdarzenia Players.PlayerAdded:Connect, a następnie ponownie wywołuje funkcję spawnPlayersInMap, aby dodać ich do drużyny z najmniejszą liczbą graczy. Aby uzyskać więcej informacji na ten temat, zobacz Skonfiguruj lokalizacje spawnowania z poprzedniej sekcji Spawnowanie i Respawnowanie samouczka.
-- Spawnuje wszystkich graczy na mapie
neutralSpawn.Neutral = false
spawnPlayersInMap(Players:GetPlayers())
-- Spawnuje nowych graczy na mapie, gdy dołączają
local playerAddedConnection = Players.PlayerAdded:Connect(function(player: Player)
spawnPlayersInMap({ player })
end)Ustal cel
Teraz, gdy każdy gracz jest na arenie ze swoimi kolegami z drużyny, gra musi dostarczyć instrukcje, co zrobić, aby odnieść sukces w rundzie. Przykładowa gra w laser tag spełnia ten wymóg, dostarczając komunikat o celu na górze ekranu każdego gracza z wyraźnymi wskazówkami, co drużyna musi zrobić, aby wygrać.

Chociaż możesz dowiedzieć się więcej o tym, jak skonfigurować i wyświetlić komponent Objective UI w programie UI Curriculum, ta sekcja koncentruje się na tym, jak wdrożyć cel jako runda się zaczyna, zaczynając od tego, jak ustawić liczbę punktów, które każda drużyna musi zdobyć, aby zakończyć rundę.
Mimo że komunikat o celu w czasie rzeczywistym informuje graczy, że muszą zdobyć trzy punkty, aby wygrać, jeśli przyjrzysz się komunikatowi w StarterGui ⟩ HUDGui, zobaczysz, że zawiera on konfigurowalny "%d" dla wartości punktowej.

"%d" to ciąg zastępczy, 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 ustawisz tę liczbę na nadmiernie wysoką 200, komunikat i licznik punktów drużyny zaktualizują się odpowiednio.
local TEAM_SCORE_LIMIT = 200 -- zaktualizowana linia, upewnij się, że zmienisz z powrotem
return 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óc zamienić ciąg zastępczy w obiekcie TextLabel UI Objective.
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ę, gra musi śledzić punkty każdej drużyny, aż osiągną swój cel. Chociaż domyślne zachowanie usługi Teams automatycznie grupuje każdego gracza pod ich drużyną i sumuje wkłady każdego gracza do wyniku drużyny, ważne jest, aby przechowywać i monitorować punkty w osobnej lokalizacji dla rozgrywki opartej na rundach, ponieważ jeśli gracz zdobędzie punkty, a następnie opuści grę przed zakończeniem rundy, ich wkład zostanie odjęty od tablicy wyników, gdy tylko się rozłączy.
Aby upewnić się, że to się nie stanie i każdy wkład w cel drużyny jest zachowany, ReplicatedStorage ⟩ HUDGuiSetup ⟩ StartSyncingTeamPoints przechowuje wszystkie punkty osobno pod atrybutem teamPoints w usłudze Teams. Gdy teamPoints się zwiększa, ten modułowy skrypt wywołuje funkcję startSyncingTeamPoints, aby znaleźć licznik drużyny Class.GuiObjects w komponencie UI Objective.
Gdy znajdzie TeamACounter i TeamBCounter, uzyskuje ich atrybut teamColor, który odpowiada strefom spawnowania drużyn: TeamACounter wyświetla punkty drużyny zielonej, a TeamBCounter śledzi punkty drużyny różowej.
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łowy skrypt następnie wywołuje swoją funkcję getTeamFromTeamColor, aby zweryfikować, że atrybut teamColor mint licznika TeamACounter i atrybut teamColor carnation pink licznika TeamBCounter odpowiadają właściwościom Team.Color w usłudze Teams. Jeśli tak, zwraca obie drużyny.
local function getTeamFromTeamColor(teamColor: Color3): Team?
for _, team in Teams:GetTeams() do
if team.TeamColor == teamColor then
return team
end
end
return nil
endGdy to nastąpi, startSyncingTeamPoints ustawia obiekty TextLabel obu liczników drużyn na odpowiadające im wartości teamPoints i kontynuuje aktualizację ich za każdym razem, gdy gracz zdobywa punkt, eliminując innego gracza z przeciwnej drużyny.
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 koncentrowało się na tym, jak śledzić punkty na ekranie gracza, ale ważne jest, aby przejrzeć logikę, która obsługuje śledzenie punktów na serwerze, aby wiedział, kiedy drużyna osiąga cel i wygrywa rundę. Jeśli wrócisz do ServerScriptService ⟩ Gameplay ⟩ Scoring, zobaczysz, że modułowy skrypt zaczyna od utworzenia zdarzenia bindable, które będzie wywoływane za każdym razem, gdy gracz zdobędzie punkt.
local 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 dodaje jeden.
- Wywołuje wcześniej wspomniane zdarzenie bindable z drużyną gracza i ich wynikiem.
Ten proces skutecznie utrzymuje zgodność zarówno klienta, jak i serwera w odniesieniu do indywidualnych wyników graczy i ich wyników drużynowych.
function Scoring.incrementScore(player: Player, amount: number)
local team = player.Team
assert(team, `Gracz {player.Name} musi być w drużynie, aby zdobyć punkt, ale nie ma drużyny`)
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)
endWyświetl wyniki
Gdy gracze eliminują się nawzajem i zdobywają punkty dla swojej drużyny, ServerScriptService ⟩ Gameplay ⟩ Rounds sprawdza, czy drużyna, która zdobyła punkty, osiągnęła cel rundy. Jeśli ich wynik drużyny jest niższy niż zmienna TEAM_SCORE_LIMIT w ReplicatedStorage ⟩ TEAM_SCORE_LIMIT, serwer nadal czeka, aż jedna z drużyn zdobędzie punkty ponownie.
Jednak gdy wynik drużyny osiągnie zmienną TEAM_SCORE_LIMIT, skrypt wywołuje instancję zdarzenia roundWinnerRemote z nazwiskiem gracza i ich drużyną.
-- Sprawdź, czy runda się zakończyła po każdym zdobyciu punktu
local team: Team
local score: number = 0
while score < TEAM_SCORE_LIMIT do
team, score = Scoring.teamScoreChanged:Wait()
end
-- Wyświetl zwycięską drużynę
for _, player in Players:GetPlayers() do
-- Wysyłanie, w jakiej drużynie jest gracz na koniec rundy
-- ponieważ drużyna gracza ma zostać usunięta, więc klient
-- nie będzie mógł sprawdzić swojej drużyny
roundWinnerRemote:FireClient(player, team, player.Team)
endSkrypt ReplicatedStorage ⟩ RoundResultsGuiSetup na każdym kliencie nasłuchuje na to zdarzenie roundWinnerRemote, aby móc:
- Wyświetlić unikalny ekran UI StarterGui ⟩ RoundResultsGui, który ogłasza wyniki rundy i informuje, czy gracz był w zwycięskiej drużynie.
- Odtworzyć dźwięk zwycięstwa lub porażki.
Na przykład, jeśli gracz jest w drużynie, która zdobyła zwycięski punkt, otrzymuje wiele form informacji zwrotnej na temat wyników rundy w postaci ekranu UI, który wyświetla tekst zwycięstwa, oraz dźwięku, który odtwarza radosny dźwięk. Z drugiej strony, jeśli gracz nie jest w drużynie, która zdobyła zwycięski punkt, otrzymuje ekran UI, który wyświetla tekst porażki, oraz dźwięk, który odtwarza złowieszczy dźwięk.
local function onRoundWinner(winner: Team, localTeam: Team?)
local victoryDefeatText = "Runda zakończona!"
if localTeam then
-- Jeśli nasza drużyna wygrała, 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
endResetuj drużyny
W tym samym czasie, gdy ServerScriptService ⟩ Gameplay ⟩ Rounds weryfikuje, że drużyna osiągnęła cel rundy i wywołuje odpowiednie wyświetlanie UI dla każdego gracza, transportuje również wszystkich graczy z areny do lobby, odłączając ich od rundy. Rozpoczyna to proces formalnego zakończenia rundy i resetowania obu drużyn.
Korzystając z tej samej logiki w Skonfiguruj lokalizacje spawnowania, Rounds ustawia właściwość Neutral lokalizacji spawnowania Neutral na true, aby gracze mogli się tam spawnować niezależnie od statusu drużyny. Oznacza to, że lobby staje się jedynym miejscem, w którym gracze mogą się spawnować po odłączeniu od rundy.
-- Wyślij wszystkich do lobby
playerAddedConnection:Disconnect()
neutralSpawn.Neutral = true
spawnPlayersInLobby(Players:GetPlayers())Po odczekaniu dziesięciu sekund na przerwę, skrypt serwera Rounds ponownie rozpoczyna pętlę, resetując wyniki wszystkich i sortując ich w nowe drużyny. Przykład powtarza ten cykliczny proces rundy, aż w serwerze nie będzie żadnych graczy.
Teraz, gdy gracze mogą się spawnować na mapie ze swoją drużyną i grać pełną rundę, następna sekcja nauczy cię o skryptach odpowiedzialnych za zachowanie każdego blastera.