Spawanie to proces tworzenia obiektu lub postaci w doświadczeniu, a odrodzenie to proces dodawania obiektu lub postaci do doświadczenia po spełnieniu warunku usunięcia, takiego jak zdrowie postaci zerowe lub upadek z mapy.Oba procesy są ważne, ponieważ zapewniają, że gracze są w stanie dołączyć do twojego doświadczenia i mogą nadal grać, aby poprawić swoje umiejętności.
Używając doświadczenia laserowego taga próbnego jako odniesienia, sekcja tego samouczka nauczy Cię, jak korzystać i dostosowywać wbudowane funkcje Roblox do obsługi spawnowania i odrodzenia, w tym wskazówek dotyczących programowania na temat:
- Konfigurowanie lokalizacji spawn, aby gracze mogli spawnować tylko w strefie spawn swojej drużyny.
- Dodawanie nowych graczy i ich postaci do rundy, gdy dołączają do doświadczenia.
- Dostosowywanie pól siły, które zapobiegają uszkodzeniom, gdy gracze się pojawiają i odrodzić.
- Zarządzanie stanem klienta, aby gra działała poprawnie we właściwym czasie.
- Odradzanie postaci po ich wykluciu z rundy.
- Wykonywanie małych, różnych działań, które są kluczowe dla ustawiania parametrów rozgrywki i postaci.
Ta sekcja zawiera dużo treści skryptowych, ale zamiast pisać wszystko od podstaw przy tworzeniu doświadczenia, zachęca do wykorzystania istniejących komponentów, szybkiego powtarzania i określenia, które systemy potrzebują niestandardowej implementacji, aby dopasować do wizji.Po zakończeniu tej sekcji nauczysz się, jak wdrożyć grę opartą na rundach, która śledzi punkty, monitoruje stan gracza i wyświetla wyniki rundy.
Konfiguruj lokalizacje spawnu
Jeśli teraz przetestujesz doświadczenie, wszyscy gracze losowo pojawią się w strefie spawnu zielonej drużyny lub strefie spawnu różowej drużyny.Oznacza to problem z rozgrywką, w którym gracze mogą oznaczać siebie nawzajem w każdej strefie spawnu, gdy tylko zniknie pole siłowe przeciwnika.
Aby rozwiązać ten problem, próbny tag laserowy konfiguruje oba miejsca spawnowania za pomocą właściwości z ustawieniem fałsz ograniczającym graczy przeciwnej drużyny do spawnowania w niewłaściwej strefie spawnu, a właściwość ustawioną na odpowiadającą wartość z przypisania kolorów drużyny w poprzedniej sekcji samouczka:
- TeamASpawn – Lokalizacja spawnu w strefie spawnu zielonego zespołu z właściwością TeamColor ustawioną na Mint .
- TeamBSpawn – Lokalizacja spawnu w strefie spawnu różowego zespołu z właściwością TeamColor ustawioną na Carnation Pink .


Kiedy gracz dołącza do doświadczenia, ServerScriptService > Gameplay > Rounds > spawnPlayersInMap sprawdza, ile graczy jest już w każdej drużynie, a następnie zwraca drużynę z najmniejszą liczbą graczy.
spawne graczy na mapie
local function getSmallestTeam(): Team
local teams = Teams:GetTeams()
-- Sortuj drużyny w kolejności od najmniejszej do największej
table.sort(teams, function(teamA: Team, teamB: Team)
return #teamA:GetPlayers() < #teamB:GetPlayers()
end)
-- Wróć do najmniejszego zespołu
return teams[1]
end
Gdy pozna zespół z najmniejszą ilością graczy, sortuje gracza do tego zespołu, ustawia jego właściwość Player.Neutral na fałsz , tak że gracz może pojawić się i odrodzić tylko w lokalizacji spawnu swojej drużyny, a następnie ustawia jego PlayerState na SelectingBlaster, o czym dowiesz się później w samouczku.
spawne graczy na mapie
local function spawnPlayersInMap(players: { Player })
for _, player in players do
player.Team = getSmallestTeam()
player.Neutral = false
player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster)
task.spawn(function()
player:LoadCharacter()
end)
end
end
Jeśli przeanalizujesz Przestrzeń roboczą > Świat > Mapę > Spawny , możesz zobaczyć, że na mapie jest jeszcze jedno miejsce odrodzenia: NeutralSpawn .To miejsce odrodzenia jest unikalne wśród pozostałych, ponieważ nie ma ustawionej właściwości TeamColor w jednej z dwóch drużyn w doświadczeniu; zamiast tego to miejsce odrodzenia ma właściwość Neutral, która zmienia się w zależności od tego, czy runda jest aktywna.
Na przykład, jeśli runda jest aktywna, właściwość Neutral ustawia się na fałsz tak, że spawnPlayersInMap może sortować graczy do drużyn i wprowadzać ich na arenę.Jeśli jednak runda nie jest aktywna, na przykład czas między jedną rundą a następną, właściwość Neutral ustawia się na prawdę , tak że gracze mogą się tam pojawić niezależnie od ich statusu zespołu.Proces ten jest tym, co czyni lokalizację spawnowania neutralną , stając się funkcjonalnym lobby.

Aby pokazać, jeśli przeanalizujesz ServerScriptService > Grę > Rundy > SpawnPlayersInLobby , który uruchamia się pod koniec rundy, możesz zobaczyć, że dla każdego gracza, który zostanie przekazany do tabeli , skrypt:
- Ustawia ich właściwość Player.Neutral na prawdę, aby automatycznie zresetować ich Player.Team do nil, umożliwiając graczowi odrodzenie się w lobby, gdy runda nie jest aktywna, ponieważ właściwość Neutral położenia spawnu również jest ustawiona na prawdę .
- Zmienia ich PlayerState na InLobby aby usunąć wizualizacje blastera gracza i interfejsu w pierwszej osobie
Aby uzyskać więcej informacji o neutralnej strefie spawnu i jej funkcjonalności dla każdej rundy, zobacz Dodawanie rund w następnej sekcji samouczka.
spawne graczy w lobby
local function spawnPlayersInLobby(players: { Player })
for _, player in players do
player.Neutral = true
player:SetAttribute(PlayerAttribute.playerState, PlayerState.InLobby)
task.spawn(function()
player:LoadCharacter()
end)
end
end
Połącz nowych graczy
Kod Luau w Studio jest często oparty na wydarzeniach, co oznacza, że skrypty słuchają wydarzeń z usługi Roblox, a następnie wzywają funkcję w odpowiedzi.Na przykład, dodając nowych graczy do doświadczenia wieloosobowego, musi istnieć wydarzenie, które obsługuje wszystko, co niezbędne do pomyślnego połączenia graczy.W przykładowym doświadczeniu z etykietą laserową, to odpowiadające wydarzenie jest Players.PlayerAdded:Connect .
Players.PlayerAdded:Connect jest częścią wielu skryptów w doświadczeniu.Jeśli użyjesz skrótu Ctrl/Cmd+Shift+F i wyszukasz Players.PlayerAdded:Connect, wyniki stanowią dobry punkt wyjścia do zrozumienia początkowej konfiguracji doświadczenia.

Aby to pokazać, otwórz ServerScriptService > SetupHumanoid .Różnica między Player a Character jest kluczowa do zrozumienia tego skryptu:
- Gracz jest połączonym klientem i postać jest modelem >.
- Gracze muszą wybrać blaster i zostać dodani do rankingwyników. Postacie muszą się pojawić i otrzymać blaster.
SetupHumanoid natychmiast sprawdza, czy gracz ma postać (właśnie dołączył) lub nie (ponownie się odrodził).Po znalezieniu jednego, dzwoni onCharacterAdded() , otrzymuje model Humanoid od postaci i przekazuje go do ServerScriptService > SetupHumanoid > setupHumanoidAsync dla dostosowania.Po ustawieniu tych wartości skrypt czeka następnie, aż zdrowie postaci osiągnie zero.Dowiesz się więcej o odrodzeniu później w tej sekcji samouczka.
ustawienieHumanoidAsync
local function setupHumanoidAsync(player: Player, humanoid: Humanoid)
humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.Subject
humanoid.NameDisplayDistance = 1000
humanoid.HealthDisplayDistance = 1000
humanoid.NameOcclusion = Enum.NameOcclusion.OccludeAll
humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOn
humanoid.BreakJointsOnDeath = false
humanoid.Died:Wait()
onHumanoidDied(player, humanoid)
end
Ważną uwagą z tym skryptem jest to, że właściwości są całkowicie opcjonalne, co oznacza, że jeśli usuniesz pierwsze sześć linii funkcji, doświadczenie nadal działa prawidłowo.Zamiast być wymaganiami funkcjonalnymi, każda właściwość pozwala podjąć decyzje projektowe, które spełniają cele gry.Na przykład:
- Jeśli chcesz, aby nazwy postaci wyświetlały się na bliższych odległościach, zmniejsz wartość Humanoid.NameDisplayDistance.
- Jeśli chcesz wyświetlić tylko zdrowie postaci, jeśli jest poniżej 100%, ustaw Humanoid.HealthDisplayType na Wyświetlaj, gdy jest uszkodzony .
- Jeśli chcesz, aby postacie rozpadły się, gdy ich zdrowie osiągnie 0, ustaw Humanoid.BreakJointsOnDeath na Prawda .
Jeśli zmienisz wartości tych właściwości, ważne jest przeprowadzenie testów, abyś mógł zobaczyć wpływ swoich nowych ustawień.Możesz odtworzyć to, co doświadczają gracze w środowisku wieloosobowym, wybierając co najmniej dwie postacie w sekcji Klienty i serwery w zakładce Test .

Kolejnym przykładem wydarzenia Players.PlayerAdded:Connect jest w ServerScriptService > PlayerStateHandler .Tak jak w poprzednim przykładzie, PlayerStateHandler natychmiast sprawdza znak.Jeśli gracz nie jest w lobby, skrypt ustawia atrybut gracza na stan SelectingBlaster , początkowy stan rundy, w której gracze mogą wybrać jeden z dwóch różnych typów blasterów po wygenerowaniu na arenie.Ten stan obejmuje również pole siłowe, które uniemożliwia graczom otrzymywanie obrażeń, gdy dokonują wyboru.
GraczStateHandler
local function onPlayerAdded(player: Player)
player.CharacterAdded:Connect(function()
if not player.Neutral then
player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster)
onPlayerStateChanged(player, PlayerState.SelectingBlaster)
end
end)
Jedna konkretna zmienna w PlayerStateHandler wojnie wymaga dyskusji: attributeChangedConnectionByPlayer .Ta tabela przechowuje wszystkich graczy i ich Connections do GetAttributeChangedSignal.Powodem przechowywania tej połączenia w tabeli jest to, że PlayerStateHandler może odłączyć się gdy gracz opuści doświadczenie.Proces ten służy jako rodzaj zarządzania pamięcią, aby zapobiec wzrostowi liczby połączeń wraz z upływem czasu.
GraczStateHandler
local attributeChangedConnectionByPlayer = {}
local function onPlayerAdded(player: Player)
-- Ręczne zarządzanie wszystkimi przyszłymi aktualizacjami stanu gracza
attributeChangedConnectionByPlayer[player] = player
:GetAttributeChangedSignal(PlayerAttribute.playerState)
:Connect(function()
local newPlayerState = player:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(player, newPlayerState)
end)
end
-- Odłączenie od zmienionej połączenia atrybutu, gdy gracz odchodzi
local function onPlayerRemoving(player: Player)
if attributeChangedConnectionByPlayer[player] then
attributeChangedConnectionByPlayer[player]:Disconnect()
attributeChangedConnectionByPlayer[player] = nil
end
end
Możesz zobaczyć, że obie połączone funkcje w wezwaniu onPlayerAdded() wzywają onPlayerStateChanged().Podczas początkowej konfiguracji po sortowaniu gracza do zespołu, onPlayerAdded() ustawia PlayerState na SelectingBlaster, więc pierwsze oświadczenie if ocenia się na fałsz i wyłącza BlasterState .W późniejszej sekcji Implementuj blastery samouczka dowiesz się więcej szczegółów na temat tego procesu.
GraczStateHandler
local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- Stan blastera jest "Gotowy", tylko jeśli stan gracza jest "Grasz"
local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled
-- Umów harmonogram logiki pola siły zniszczenia, gdy gracz zaczyna grać
if newPlayerState == PlayerState.Playing then
scheduleDestroyForceField(player)
end
player:SetAttribute(PlayerAttribute.blasterStateServer, newBlasterState)
end
Jeśli dodasz punkty przerwania lub nawet tylko oświadczenie print(), możesz zobaczyć, że onPlayerStateChanged() jest często wzywany w trakcie doświadczenia: na przykład podczas początkowej konfiguracji rundy, aby ustawić się na głównej ścieżce kodu, po wyborze przez gracza blastera i po powrocie gracza do lobby lub do lokalizacji Neutral .Ponadto, po wybraniu przez gracza blastera, ServerScriptService > BlasterSelectedHandler > ustawia PlayerState na Playing, a PlayerStateHandler może w końcu usunąć pole siły, wezwając scheduleDestroyForceField().
Dostosuj pola siły
Zamiast używać niestandardowej implementacji, próbna etykieta laserowa używa klasy ForceField wbudowanej w Studio, aby zapobiec obrażeniom graczy, gdy wybierają swojego blastera.Zapewnia to, że jedynym wymogiem dla graczy, aby pojawiły się z polem siłowym, jest uwzględnienie lokalizacji spawn z właściwością SpawnLocation.Duration, która jest większa niż 0.Próbka używa losowej wartości 9,999, aby włączyć pola siły, a następnie obsługuje rzeczywisty czas programistycznie w ReplicatedStorage > ForceFieldClientVisuals .
Podobnie jak setupHumanoidAsync, większość linii w ForceFieldClientVisuals jest opcjonalna.Na przykład, jeśli skomentujesz zawartość funkcji tak, jak to robi następujący skrypt, doświadczenie używa domyślnego błyszczącego pola siły zamiast heksagonalnego skryptu w StarterGui > ForceFieldGui .
Komentowanie właściwości w ForceFieldClientVisuals
local function onCharacterAddedAsync(character: Model)
-- lokalne pole siły = postać:WaitForChild("ForceField"), 3
-- jeśli nie forceField, wówczas
-- powrót
-- kończyć
-- forceField.Visible = fałsz
-- localPlayer.PlayerGui:WaitForChild("ForceFieldGui") Włączone = prawda
-- forceField.Destroying:Wait()
-- localPlayer.PlayerGui.ForceFieldGui.Enabled = fałsz
end
Ponieważ niestandardowe pole siłowe jest interfejsem graficznym, a nie nowym ParticleEmitter, skrypt ForceFieldClientVisuals wpływa tylko na wizualizacje pierwszej osoby dla każdego gracza, nie wizualizacje trzeciej osoby, gdy gracze patrzą na innych graczy.Wizualizacje z perspektywy trzeciej zachowują wyglądaparycję Roblox.Aby uzyskać więcej informacji na temat modyfikowania pól siły, zobacz ForceField.Visible .


Pola siłowe są przydatne, ponieważ zapewniają graczom wystarczająco dużo czasu na pomiędzy odrodzeniem a odrodzeniem bez konieczności martwienia się o wrogich graczy, ale ostatecznie muszą zniknąć dla głównego laserowego tagowania rozgrywka.Skrypt, który zajmuje się usuwaniem pola siły, znajduje się w ReplicatedStorage > scheduleDestroyForceField i sprawdza trzy unikalne warunki:
- Po wybraniu przez graczy blastera pola siłowe muszą istnieć wystarczająco długo, aby gracze mogli się przyzwyczaić do swojego otoczenia.
- Podczas tego czasu aklimatyzacji pola siłowe nie mogą być zaletą, więc muszą zniknąć w momencie, gdy gracz wystrzeli swój blaster.
- Pola siły muszą zniknąć, gdy gracze zresetują swoje postacie przed wybuchaniem lub przed wygaśnięciem czasu pola siły.
Każda z tych kontroli w wezwaniu skryptu dla tych warunków.
planujesz niszczyć pole siły
-- Zakończ pole siłowe, jeśli gracz wystrzeli
local blasterStateAttribute = getBlasterStateAttribute()
attributeChangedConnection = player:GetAttributeChangedSignal(blasterStateAttribute):Connect(function()
local currentBlasterState = player:GetAttribute(blasterStateAttribute)
if currentBlasterState == BlasterState.Blasting then
endForceField()
end
end)
-- Zakończ pole siłowe, jeśli gracz zresetuje
characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField)
-- Zakończ pole siłowe po 8 sekundach
task.delay(MAX_FORCE_FIELD_TIME, endForceField)
endForceField() zawiera zdawałoby się dziwną if deklarację wokół forceFieldEnded prawdziwego binarnego.Ponieważ kontrolki uruchamiane są kolejno, skrypt może wezwać funkcję endForceField() dwukrotnie lub nawet trzykrotnie.Funkcja forceFieldEnded booleanowa zapewnia, że funkcja próbuje zniszczyć pole siłowe tylko raz.
planujesz niszczyć pole siły
local function endForceField()
if forceFieldEnded then
return
end
forceFieldEnded = true
attributeChangedConnection:Disconnect()
characterRespawnedConnection:Disconnect()
destroyForceField(player)
end
Ręczne przechowywanie stanu klienta
Chociaż większość tej sekcji skupia się na ServerScriptService > PlayerStateHandler , istnieje inny skrypt o tej samej nazwie w ReplicatedStorage .Powodem podziału jest architektura klient-serwer:
Klient musi zrozumieć informacje o stanie gracza, aby mógł odpowiednio reagować w czasie rzeczywistym, takie jak wyświetlanie właściwych elementów interfejsu użytkownika lub umożliwianie graczom poruszania się i wystrzeliwania.
Serwer potrzebuje tej samej informacji, aby mógł zapobiegać exploitom.Na przykład serwer potrzebuje również stanu gracza do wykonywania działań, takich jak generowanie i wyposażanie postaci, wyłączanie pól siły i wyświetlanie tabeli wyników.Dlatego ten skrypt jest w ReplicatedStorage a nie w czystej lokalizacji strony klienta.
Aby zobaczyć tę podstawową logikę, przejrzyj następujący skrypt w ReplicatedStorage > PlayerStateHandler , który weryfikuje obecny stan użytkownika, a następnie dzwoni do odpowiedniej funkcji, która obsługuje odpowiednie działania dla tego stanu.
GraczStateHandler
local function onPlayerStateChanged(newPlayerState: string)
if newPlayerState == PlayerState.SelectingBlaster then
onSelectingBlaster()
elseif newPlayerState == PlayerState.Playing then
onPlaying()
elseif newPlayerState == PlayerState.TaggedOut then
onTaggedOut()
elseif newPlayerState == PlayerState.InLobby then
onInLobby()
else
warn(`Invalid player state ({newPlayerState})`)
end
end
Wszystkie odpowiedzi na wydarzenia są logicznie grupowane w tym skrypcie, ponieważ wymagają podobnego zachowania aktywowania lub wyłączania sterowania elementy sterujące, ruchu kamery i widoczności warstwy interfejsu użytkownika.Na przykład, podczas wyboru blastera, gracze muszą być zarówno niezwyciężeni, jak i niezdolni do poruszania się.Serwer już obsługuje pole siły, ale klient obsługuje ruch.Aby pokazać, jeśli sprawdzisz logikę dla funkcji onSelectingBlaster() , możesz zobaczyć, że klient wyłącza ruch gracza, gdy wybierają blaster.
GraczStateHandler
local function onSelectingBlaster()
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
Funkcja onPlaying() jest podobnie prosta.Umożliwia ruch, przejście do głównego wyświetlania na ekranie (HUD), umożliwia blaster i wywołuje tę samą funkcję pola siły jak serwer.
GraczStateHandler
local function onPlaying()
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
scheduleDestroyForceField()
end
Odrodź postacie
Próbne doświadczenie z etykietą laserową obsługuje odrodzenie postaci z powrotem do rundy za pośrednictwem stanu onTaggedOut() w ReplicatedStorage > GraczStateHandler .Podobnie jak stan onSelectingBlaster() i onPlaying(), onTaggedOut() uruchamia unikalne zachowanie zgodnie z zmianami w atrybucie playerState.W szczególności wyłącza ruch gracza, prezentuje interfejs odrodzenia i wyłącza blaster.
GraczStateHandler
local function onTaggedOut()
-- Wyłącz sterowanie podczas oznaczania
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- Wyłącz blaster podczas oznaczania
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
Jeśli chcesz przetestować to zachowanie, możesz nacisnąć Esc, przejść do zakładki Ustawienia i kliknąć przycisk Zresetuj postać .Zauważ, że gdy uruchamiasz ekran odrodzenia, nie możesz się poruszać, obracać kamerę lub wystrzelić swój blaster.


Ważne jest, aby zauważyć, że ten skrypt nie rzeczywiście odrodza postacie, po prostu powstrzymuje je od działania i dostarcza wizualne informacje zwrotne graczom, że serwer odrodzi ich postacie.Aby pokazać, jeśli przeanalizujesz ServerScriptService > SetupHumanoid > setupHumanoidAsync > naHumanoidDied , skrypt ustawia PlayerState na TaggedOut (zasadniczo powiadamiając ReplicatedStorage > PlayerStateHandler ), a dodaje kilka wizualnych wskaźników.Rzeczywista logika odrodzenia jest zachowaniem wbudowanym w Roblox.
Kiedy gracze odrodzą się ponownie w rundzie, odrodzą się w lokalizacji spawnu swojej drużyny zgodnie z właściwością SpawnLocation.TeamColor.Aby dostosować czas odrodzenia, możesz dodać następną linię na górze SetupHumanoid.Aby dowiedzieć się więcej o tej technice, zobacz Players.RespawnTime .
UstawienieHumanoid
local Players = game:GetService("Players")Players.RespawnTime = 10 -- new line, in seconds
Różne ustawienia
W ramach początkowego ustawienia próbna etykieta laserowa również wykonuje kilka małych, ale krytycznych kroków:
Doświadczenie obejmuje pusty skrypt o nazwie StarterPlayer > StarterCharacterScripts > Zdrowie , który wyłącza domyślną regenerację zdrowia Roblox.Aby uzyskać wyjaśnienie zachowania tej właściwości, zobacz Humanoid.Health .
Doświadczenie wykorzystuje kamerę w pierwszej osobie, ustawiając właściwość StarterPlayer.CameraMode.LockFirstPerson.Zauważ, że jeśli chcesz pozwolić użytkownikom zmieniać między kamerami pierwszo- i trzecioludzkimi, musisz zmienić właściwość programatycznie, a nie tylko ustawić ją raz w Studio, a następnie zmodyfikować sterowanie i interfejs użytkownika, aby zrekompensować zmianę perspektywy.
Doświadczenie wykorzystuje wbudowaną tablicę wyników Roblox z jednostką "punktów", które gracze zdobywają za każdym razem, gdy oznaczają innego gracza.Możesz zobaczyć konfigurację w ServerScriptService > SetupLeaderboard , ale In-Experience Leaderboards oferuje pełny przegląd.Zauważ, że onPlayerTagged dodaje punkty do tabeli ranking, o których dowiesz się w Dodaj rundy i Wykryj trafienia.
Teraz, gdy gracze mogą się pojawiać, wybierz blaster i celuj z niego z punktu widzenia pierwszej osoby, następna sekcja nauczy cię o skryptach, które stoją za tworzeniem rozgrywkawokół rundy.