Spawning è il processo di creazione di un oggetto o di un personaggio in un'esperienza, e respawning è il processo di aggiungere un oggetto o un personaggio nuovamente in un'esperienza dopo aver soddisfatto una condizione di rimozione, come la salute di un personaggio che raggiunge lo zero o cade dalla mappa.Entrambi i processi sono importanti perché garantiscono che i giocatori siano in grado di unirsi alla tua esperienza e possano continuare a giocare per migliorare le loro abilità.
Usando l'esperienza di tag laser di esempio come riferimento, questa sezione del tutorial ti insegna come utilizzare e personalizzare le funzionalità integrate di Roblox per gestire la generazione e la rigenerazione, inclusa la guida sugli script su:
- Configurare i luoghi di spawn in modo che i giocatori possano spawnare solo nella zona di spawn della loro team.
- Aggiungere nuovi giocatori e il loro personaggio alla partita mentre si uniscono all'esperienza.
- Personalizzazione dei campi di forza che impediscono danni mentre i giocatori si generano e respawnano.
- Gestire lo stato del cliente in modo che il gameplay funzioni correttamente al momento appropriato.
- Rigenerazione dei personaggi dopo che sono stati etichettati fuori dal round.
- Eseguire piccole azioni miscellanee che sono cruciali per impostare i parametri di gioco e di personaggio.
Questa sezione include un sacco di contenuto di scripting, ma invece di scrivere tutto da zero quando crei un'esperienza, ti incoraggia a sfruttare componenti esistenti, a iterare rapidamente e a capire quali sistemi hanno bisogno di una implementazione personalizzata per corrispondere alla tua visione.Dopo aver completato questa sezione, imparerai a implementare il gameplay basato sul round che traccia i punti, monitora lo stato del giocatore e visualizza i risultati del round.
Configura i luoghi di spawn
Se volessi testare l'esperienza in questo momento, tutti i giocatori sarebbero generati casualmente nell'area di spawn dell'oggetto SpawnLocation o nell'area di spawn dell'oggetto SpawnLocation della teamverde.Questo presenta un problema di gameplay in cui i giocatori potrebbero etichettarsi l'un l'altro all'interno di ogni zona di spawn non appena il campo di forza dell'avversario scompare.
Per combattere questo problema, l'esperienza di tag laser di esempio configura entrambi i luoghi di spawn con una proprietà impostata su falso per limitare i giocatori dell'teamavversaria dallo spawn nella zona di spawn sbagliata e una proprietà impostata sul valore corrispondente di Assign Team Colors nella sezione precedente del Tutoriale:
- TeamASpawn – La posizione di spawn nella zona di spawn della teamverde con una proprietà TeamColor impostata su Menta .
- TeamBSpawn – La posizione di spawn nella zona di spawn della teamrosa con una proprietà TeamColor impostata su Carnation Pink .


Quando un giocatore si unisce all'esperienza, ServerScriptService > Gameplay > Round > spawnPlayersInMap controlla per vedere quanti giocatori sono già in ogni team, quindi restituisce la squadra con il minor numero di giocatori.
Genera giocatori in mappa
local function getSmallestTeam(): Team
local teams = Teams:GetTeams()
-- Ordina le squadre in ordine crescente dal più piccolo al più grande
table.sort(teams, function(teamA: Team, teamB: Team)
return #teamA:GetPlayers() < #teamB:GetPlayers()
end)
-- Restituisci la teampiù piccola
return teams[1]
end
Una volta che conosce la squadra con la minore quantità di giocatori, ordina il giocatore nella team, imposta la loro proprietà falsa perché il giocatore può generare e rigenerare solo nella posizione di spawn della team, quindi imposta la loro proprietà falsa in , che imparerai di più in seguito nel Tutoriale.
Genera giocatori in mappa
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
Se esamini Spazio di lavoro > Mondo > Mappa > Spawns , puoi vedere che c'è un'altra posizione di spawn nella mappa: Spawn Neutrale .Questa posizione di spawn è unica rispetto alle altre perché non ha un set di proprietà TeamColor a uno dei due team nell'esperienza; invece, questa posizione di spawn ha una proprietà Neutral che cambia a seconda se una partita è attiva.
Ad esempio, se il round è attivo, la proprietà Neutral imposta su falso in modo che spawnPlayersInMap possa ordinare i giocatori in team e generarli nell'arena.Tuttavia, se il round non è attivo, come il tempo tra un round e il successivo, la proprietà Neutral imposta su vero in modo che i giocatori possano generarsi lì indipendentemente dallo Statodella loro squadra.Questo processo è ciò che rende il Neutrale luogo di spawn una lobby funzionale.

Per dimostrare, se esamini ServerScriptService > Gameplay > Round > SpawnPlayersInLobby , che viene eseguito alla fine di un round, puoi vedere che per ogni giocatore che viene passato nella tabella players: { Player }, lo script:
- Imposta la loro proprietà a true per ripristinare automaticamente il loro a , consentendo al giocatore di respawn nella lobby quando una partita non è attiva, poiché la proprietà del luogo di spawn è anche impostata su true .
- Cambia il loro PlayerState in InLobby per rimuovere le visuali dell'interfaccia utente in prima persona e il blaster del Giocatore.
Per maggiori informazioni sulla zona di spawn neutrale e sulla sua funzionalità per ogni round, vedi Aggiungere round nella sezione successiva del Tutoriale.
Genera giocatori nella 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
Connetti nuovi giocatori
Il codice Luau in Studio è spesso event-driven, cioè gli script ascoltano gli eventi da un servizio Roblox, quindi chiamano una funzione in risposta.Ad esempio, quando si aggiungono nuovi giocatori a un'esperienza multiplayer, deve esserci un evento che gestisca tutto ciò che è necessario per i giocatori per connettersi con successo.Nell'esperienza di tag laser di esempio, questo evento corrispondente è Players.PlayerAdded:Connect .
Players.PlayerAdded:Connect è una parte di più script nell'esperienza.Se utilizzi la scorciatoia Ctrl/Cmd+Shift+F e cerchi Players.PlayerAdded:Connect , i risultati forniscono un buon punto di partenza per comprendere l'installazione iniziale dell'esperienza.

Per dimostrare, apri ServerScriptService > SetupHumanoid .La distinzione tra Player e Character è chiave per comprendere questo script:
- I giocatori devono scegliere un blaster e essere aggiunti alla Classifica. I personaggi devono spawnare e ricevere un blaster.
SetupHumanoid controlla immediatamente se il giocatore ha un personaggio (appena entrato) o non lo ha (si sta rigenerando).Dopo aver trovato uno, chiama onCharacterAdded() , ottiene il modello Humanoid dal personaggio e lo passa a ServerScriptService > SetupHumanoid > setupHumanoidAsync per la personalizzazione.Dopo aver impostato questi valori, lo script poi attende che la salute del personaggio raggiunga lo zero.Imparerai di più sul respawn più tardi in questa sezione del Tutoriale.
setupHumanoidAsync
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
L'importante nota con questo script è che le proprietà sono completamente opzionali, il che significa che se rimuovi le prime sei righe della funzione, l'esperienza funziona ancora correttamente.Piuttosto che essere requisiti funzionali, ogni proprietà ti consente di prendere decisioni di progettazione che soddisfino i tuoi obiettivi di gioco.Ad esempio:
- Se vuoi che i nomi dei personaggi vengano visualizzati a distanze più vicine, riduci il valore di Humanoid.NameDisplayDistance .
- Se vuoi solo la salute di un personaggio da mostrare se è inferiore al 100%, imposta Humanoid.HealthDisplayType a DisplayWhenDamaged .
- Se vuoi che i personaggi si dividano quando la loro salute raggiunge 0, imposta Humanoid.BreakJointsOnDeath a Verità .
Se cambi i valori di queste proprietà, è importante testare per vedere l'impatto delle tue nuove impostazioni.Puoi ricreare l'esperienza che i giocatori vivono in un ambiente multiplayer selezionando almeno due personaggi nella sezione Client e Server della scheda Test .

Un altro esempio dell'evento Players.PlayerAdded:Connect è in ServerScriptService > PlayerStateHandler .Proprio come nell'esempio precedente, PlayerStateHandler controlla immediatamente un personaggio.Se il giocatore non è nella lobby, lo script imposta un attributo del giocatore allo stato SelectingBlaster , lo stato iniziale per un round in cui i giocatori possono selezionare da uno dei due diversi tipi di blaster dopo essere stati generati nell'arena.Questo stato include anche un campo di forza che impedisce ai giocatori di subire danni mentre stanno facendo la loro selezione.
GiocatoreStateHandler
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)
Una variabile particolare in PlayerStateHandler garantisce una discussione: attributeChangedConnectionByPlayer .Questa tabella memorizza tutti i giocatori e le loro Connections a GetAttributeChangedSignal .Il motivo per cui viene memorizzata questa connessione in una tabella è così che PlayerStateHandler può disconnettersi quando il giocatore lascia l'esperienza.Questo processo serve come una sorta di gestione della memoria per prevenire il numero di connessioni da crescere sempre più grandi nel tempo.
GiocatoreStateHandler
local attributeChangedConnectionByPlayer = {}
local function onPlayerAdded(player: Player)
-- Gestisci tutti gli aggiornamenti futuri dello stato del giocatore
attributeChangedConnectionByPlayer[player] = player
:GetAttributeChangedSignal(PlayerAttribute.playerState)
:Connect(function()
local newPlayerState = player:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(player, newPlayerState)
end)
end
-- Disconnetti dalla connessione modificata dall'attributo quando il giocatore esce
local function onPlayerRemoving(player: Player)
if attributeChangedConnectionByPlayer[player] then
attributeChangedConnectionByPlayer[player]:Disconnect()
attributeChangedConnectionByPlayer[player] = nil
end
end
Puoi vedere che entrambe le funzioni connesse in onPlayerAdded() chiamata onPlayerStateChanged() .Durante l'installazione iniziale dopo che un giocatore si classifica in una team, onPlayerAdded() imposta PlayerState a SelectingBlaster , quindi la prima dichiarazione if viene valutata a falso e disabilita il BlasterState .Nella sezione successiva Implementa blaster del Tutoriale, imparerai maggiori dettagli su questo processo.
GiocatoreStateHandler
local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- Lo stato del blaster è 'Pronto' solo se lo stato del giocatore è 'Giocando'
local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled
-- Programma la logica del campo di forza distruttivo quando il giocatore inizia a giocare
if newPlayerState == PlayerState.Playing then
scheduleDestroyForceField(player)
end
player:SetAttribute(PlayerAttribute.blasterStateServer, newBlasterState)
end
Se aggiungi punti di interruzione o anche solo una dichiarazione print(), puoi vedere che onPlayerStateChanged() viene chiamata frequentemente durante l'esperienza: come durante l'installazione iniziale di un round, per impostarsi sul percorso principale del codice, dopo che il giocatore sceglie un blaster, e quando il giocatore ritorna alla lobby, o sul luogo di spawn Neutrale .Inoltre, dopo che il giocatore sceglie un blaster, ServerScriptService > BlasterSelectedHandler imposta il PlayerState a Playing, e PlayerStateHandler può infine rimuovere il campo di forza chiamando scheduleDestroyForceField() .
Personalizza i campi di forza
Invece di utilizzare una implementazione personalizzata, l'esperienza di tag laser di esempio utilizza la classe integrata di Studio ForceField per impedire ai giocatori di subire danni mentre selezionano il loro blaster.Questo garantisce che l'unico requisito per i giocatori di spawnare con un campo di forza è quello di includere le posizioni di spawn con una proprietà SpawnLocation.Duration superiore a 0.L'esempio utilizza un valore arbitrario di 9,999 per abilitare i campi di forza, quindi gestisce la durata effettiva in modo programmatico in ReplicatedStorage > ForceFieldClientVisuals .
Simile a setupHumanoidAsync , la maggior parte delle linee in ForceFieldClientVisuals sono opzionali.Ad esempio, se commenti i contenuti della funzione come fa il seguente script, l'esperienza utilizza il campo di forza scintillante predefinito invece dello script esagonale in StarterGui > ForceFieldGui .
Commentare le proprietà in ForceFieldClientVisuals
local function onCharacterAddedAsync(character: Model)
-- forceField locale = personaggio:WaitForChild("ForceField", 3)
-- se non forceField allora
-- riportare
-- terminare
-- forceField.Visible = falso
-- localPlayer.PlayerGui:WaitForChild("ForceFieldGui").Enabled = verità
-- forceField.Destroying: Wait()
-- localPlayer.PlayerGui.ForceFieldGui.Enabled = false
end
Poiché il campo di forza personalizzato è una GUI piuttosto che un nuovo ParticleEmitter, lo script ForceFieldClientVisuals influisce solo sui visuali in prima persona per ciascun Giocatore, non sui visuali in terza persona quando i giocatori guardano altri giocatori.Le visuali in terza persona mantengono l'aspetto predefinito di Roblox.Per ulteriori informazioni sulla modifica dei campi di forza, vedi ForceField.Visible .


I campi di forza sono utili perché forniscono ai giocatori abbastanza tempo per traformarsi e rigenerarsi senza doversi preoccupare dei giocatori nemici, ma alla fine devono scomparire per il principale Partitadi tag laser principale.Lo script che gestisce la rimozione del campo di forza è in ReplicatedStorage > scheduleDestroyForceField , e controlla per tre condizioni uniche:
- Dopo che i giocatori hanno selezionato un blaster, i campi di forza devono durare abbastanza a lungo da consentire ai giocatori di acclimatarsi ai loro dintorni.
- Durante questo periodo di acclimazione, i campi di forza non possono essere un vantaggio, quindi devono scomparire nel momento in cui un giocatore lancia il suo blaster.
- I campi di forza devono scomparire quando i giocatori ripristinano i loro personaggi prima di esplodere o prima che il campo di forza scada.
Ognuno di questi controlli nella chiamata dello script per queste condizioni.
scheduleDestroyForceField
-- Termina il campo di forza se il giocatore esplode
local blasterStateAttribute = getBlasterStateAttribute()
attributeChangedConnection = player:GetAttributeChangedSignal(blasterStateAttribute):Connect(function()
local currentBlasterState = player:GetAttribute(blasterStateAttribute)
if currentBlasterState == BlasterState.Blasting then
endForceField()
end
end)
-- Termina il campo di forza se il giocatore si ripristina
characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField)
-- Campo di forza finale dopo 8 secondi
task.delay(MAX_FORCE_FIELD_TIME, endForceField)
endForceField() include una dichiarazione apparentemente strana if attorno al forceFieldEnded booleano.Poiché i controlli vengono eseguiti in sequenza, lo script può chiamare la funzione endForceField() due o anche tre volte.Il forceFieldEnded booleano garantisce che la funzione provi a distruggere un campo di forza solo una volta.
scheduleDestroyForceField
local function endForceField()
if forceFieldEnded then
return
end
forceFieldEnded = true
attributeChangedConnection:Disconnect()
characterRespawnedConnection:Disconnect()
destroyForceField(player)
end
Gestisci lo stato del client
Mentre la maggior parte di questa sezione si concentra su ServerScriptService > PlayerStateHandler , c'è un altro script con lo stesso nome in ReplicatedStorage .La ragione della divisione è l'architettura client-server:
Il client deve comprendere le informazioni sullo stato del giocatore in modo che possa rispondere in modo appropriato in tempo reale, come mostrare gli elementi dell'interfaccia utente giusta o abilitare i giocatori a muoversi e sparare.
Il server ha bisogno di tutte queste stesse informazioni per poter prevenire gli exploit.Ad esempio, il server ha anche bisogno dello stato del giocatore per eseguire azioni come la generazione e l'equipaggiamento di personaggi, la disattivazione dei campi di forza e la visualizzazione di una Classifica.Questo è il motivo per cui questo script è in ReplicatedStorage e non in una posizione puramente lato client.
Per vedere questa logica principale, controlla lo script seguente in ReplicatedStorage > PlayerStateHandler che verifica lo stato attuale dell'utente, quindi chiama la funzione appropriata che gestisce le azioni corrispondenti per quell' stato.
GiocatoreStateHandler
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
Tutte le risposte agli eventi sono logicamente raggruppate insieme in questo script perché richiedono un comportamento simile di attivazione o disattivazione dei Controllidel giocatore, del movimento della fotocamera e quale strato dell'interfaccia utente è visibile.Ad esempio, durante la selezione del blaster, i giocatori devono essere sia invulnerabili che incapaci di Sposta.Il server gestisce già il campo di forza, ma il client gestisce il movimento.Per dimostrare, se controlli la logica per la funzione onSelectingBlaster() , puoi vedere che il client disabilita il movimento del giocatore mentre seleziona un blaster.
GiocatoreStateHandler
local function onSelectingBlaster()
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
La funzione onPlaying() è similmente semplice.Consente il movimento, le transizioni al display principale a schermo piatto (HUD), abilita il blaster e chiama la stessa funzione del campo di forza del Server.
GiocatoreStateHandler
local function onPlaying()
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
scheduleDestroyForceField()
end
Rigenera i personaggi
L'esperienza di tag laser di esempio che gestisce il respawning del personaggio torna in una partita attraverso lo stato onTaggedOut() in ReplicatedStorage > PlayerStateHandler .Come lo stato onSelectingBlaster() e onPlaying() , onTaggedOut() attiva un comportamento unico in base alle modifiche all'attributo playerState.In particolare, disabilita il movimento del giocatore, presenta l'interfaccia utente di respawn e disabilita il blaster.
GiocatoreStateHandler
local function onTaggedOut()
-- Disabilita i controlli mentre sei etichettato fuori
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- Disabilita il blaster mentre sei etichettato fuori
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
Se vuoi testare questo comportamento, puoi premere Esc, navigare alla scheda Impostazioni , quindi fare clic sul pulsante Ripristina personaggio .Nota che quando attivi la schermata di riproduzione, non puoi Sposta, ruotare la Telecamerao esplodere il tuo blaster.


È importante notare che questo script in realtà non respawna i personaggi, li ferma soltanto dall'agire e fornisce un feedback visivo ai giocatori che il server sta respawnando i loro personaggi.Per dimostrare, se esamini ServerScriptService > SetupHumanoid > setupHumanoidAsync > onHumanoidDied , lo script imposta PlayerState su TaggedOut (essenzialmente notificando ReplicatedStorage > PlayerStateHandler ), e aggiunge alcuni indicatori visivi.La logica effettiva di respawn è un comportamento integrato di Roblox.
Quando i giocatori riappaiono nella partita, riappaiono nella posizione di spawn della loro teamsecondo la ProprietàSpawnLocation.TeamColor.Per personalizzare il tempo di respawn, puoi aggiungere la seguente linea in cima a SetupHumanoid .Per saperne di più su questa tecnica, vedi Players.RespawnTime .
ImpostazioneHumanoid
local Players = game:GetService("Players")Players.RespawnTime = 10 -- new line, in seconds
Impostazione miscellanea
Come parte dell'attivazione iniziale, l'esperienza di tag laser di prova esegue anche alcuni piccoli, ma critici passaggi:
L'esperienza include uno script vuoto chiamato StarterPlayer > StarterCharacterScripts > Salute che disabilita la regolazione della salute predefinita di Roblox.Per una spiegazione del comportamento di questa Proprietà, vedi Humanoid.Health .
L'esperienza utilizza una fotocamera in prima persona impostando la ProprietàStarterPlayer.CameraMode.LockFirstPerson .Nota che se vuoi consentire agli utenti di cambiare tra fotocamere in prima e terza persona, devi cambiare la proprietà in modo programmatico piuttosto che impostarla una sola volta in Studio e modificare i controlli e l'interfaccia utente per compensare il cambiamento di prospettiva.
L'esperienza utilizza la classifica integrata di Roblox con l'unità di "punti", che i giocatori guadagnano ogni volta che contrassegnano un altro giocatore.Puoi vedere la configurazione in ServerScriptService > SetupLeaderboard , ma In-Experience Leaderboards offre una panoramica completa.Nota che onPlayerTagged aggiunge punti alla Classifica, che imparerai in Aggiungi round e Rileva colpi.
Ora che i giocatori possono Rigenerare, scegli un blaster e miralo da un punto di vista in prima persona, la sezione successiva ti insegna sugli script dietro alla creazione di un Partitabasato sul round.