Spawn e respawn

*Questo contenuto è tradotto usando AI (Beta) e potrebbe contenere errori. Per visualizzare questa pagina in inglese, clicca qui.

Spawn è il processo di creazione di un oggetto o personaggio in un gioco, e respawn è il processo di reinserimento di un oggetto o personaggio nel gioco dopo che ha soddisfatto una condizione di rimozione, come la salute di un personaggio che raggiunge zero o la caduta dalla mappa. Entrambi i processi sono importanti perché garantiscono che i giocatori possano unirsi al tuo gioco e continuare a giocare per migliorare le proprie abilità.

Utilizzando il gioco di laser tag di esempio come riferimento, questa sezione del tutorial ti insegna come utilizzare e personalizzare le funzionalità integrate di Roblox per gestire lo spawn e il respawn, inclusa la guida alla scrittura di script su:

  • Configurare le posizioni di spawn in modo che i giocatori possano solo spawnare nella zona di spawn del proprio team.
  • Aggiungere nuovi giocatori e il loro personaggio al turno mentre si uniscono al gioco.
  • Personalizzare i campi di forza che prevengono danni mentre i giocatori spawnano e respawnano.
  • Gestire lo stato del client in modo che il gameplay funzioni correttamente al momento appropriato.
  • Respawnare i personaggi dopo che sono stati eliminati dal turno.
  • Eseguire piccole azioni varie che sono cruciali per impostare i parametri di gioco e del personaggio.

Questa sezione include molti contenuti di scripting, ma invece di scrivere tutto da zero quando crei un gioco, ti incoraggia a sfruttare i componenti esistenti, iterare rapidamente e capire quali sistemi necessitano di un'implementazione personalizzata per corrispondere alla tua visione. Dopo aver completato questa sezione, imparerai come implementare un gameplay basato su turni che tiene traccia dei punti, monitora lo stato dei giocatori e visualizza i risultati del turno.

Configura le posizioni di spawn

Se dovessi testare il gioco in questo momento, tutti i giocatori spawnerebbero casualmente o nell'oggetto SpawnLocation nella zona di spawn del team verde, o nell'oggetto SpawnLocation nella zona di spawn del team rosa. Questo presenta un problema di gameplay in cui i giocatori potrebbero tagliarsi a vicenda all'interno di ciascuna zona di spawn non appena il campo di forza del loro avversario scompare.

Per combattere questo problema, il gioco di laser tag di esempio configura entrambe le posizioni di spawn con una proprietà Neutral impostata su false per impedire ai giocatori del team avversario di spawnare nella zona di spawn sbagliata, e una proprietà TeamColor impostata sul corrispondente valore Team.Color da Assegna colori ai team nella sezione precedente del tutorial:

  • TeamASpawn – La posizione di spawn nella zona di spawn del team verde con una proprietà TeamColor impostata su Mint.
  • TeamBSpawn – La posizione di spawn nella zona di spawn del team rosa con una proprietà TeamColor impostata su Carnation Pink.
TeamASpawn
TeamBSpawn

Quando un giocatore si unisce al gioco, ServerScriptServiceGameplayRoundsspawnPlayersInMap controlla quanti giocatori sono già in ciascun team, quindi restituisce il team con il minor numero di giocatori.

spawnPlayersInMap
local function getSmallestTeam(): Team
local teams = Teams:GetTeams()
-- Ordina i team in ordine crescente da minore a maggiore
table.sort(teams, function(teamA: Team, teamB: Team)
return #teamA:GetPlayers() < #teamB:GetPlayers()
end)
-- Restituisce il team più piccolo
return teams[1]
end

Una volta che conosce il team con il minor numero di giocatori, lo assegna a quel team, imposta la proprietà Player.Neutral su false in modo che il giocatore possa solo spawnare e respawnare nella posizione di spawn del proprio team, quindi imposta il loro PlayerState su SelectingBlaster, di cui imparerai di più più avanti nel tutorial.

spawnPlayersInMap
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 WorkspaceWorldMapSpawns, puoi vedere che c'è un'altra posizione di spawn nella mappa: NeutralSpawn. Questa posizione di spawn è unica rispetto alle altre perché non ha una proprietà TeamColor impostata su uno dei due team nel gioco; invece, questa posizione di spawn ha una proprietà Neutral che cambia a seconda se un turno è attivo.

Ad esempio, se il turno è attivo, la proprietà Neutral è impostata su false in modo che spawnPlayersInMap possa ordinare i giocatori nei team e spawnarli nell'arena. Tuttavia, se il turno non è attivo, come nel tempo tra un turno e l'altro, la proprietà Neutral è impostata su true in modo che i giocatori possano spawnare lì indipendentemente dal loro stato di team. Questo processo è ciò che rende la posizione di spawn Neutral un'area di attesa funzionale.

Neutral

Per dimostrare, se esamini ServerScriptServiceGameplayRoundsSpawnPlayersInLobby, che viene eseguito alla fine di un turno, puoi vedere che per ogni giocatore passato nella tabella players: { Player }, lo script:

  • Imposta la proprietà Player.Neutral su true per ripristinare automaticamente il Player.Team su nil, consentendo al giocatore di respawnare nella lobby quando un turno non è attivo, poiché la proprietà Neutral della posizione di spawn è anch'essa impostata su true.
  • Cambia il loro PlayerState in InLobby per rimuovere il blaster del giocatore e le visualizzazioni dell'interfaccia utente in prima persona.

Per ulteriori informazioni sulla zona di spawn neutrale e sulla sua funzionalità per ciascun turno, vedere Aggiungere turni nella sezione successiva del tutorial.

spawnPlayersInLobby
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

Collega nuovi giocatori

Il codice Luau in Studio è spesso basato su eventi, il che significa che gli script ascoltano eventi da un servizio Roblox e poi chiamano una funzione in risposta. Ad esempio, quando si aggiungono nuovi giocatori a un gioco multiplayer, deve esserci un evento che gestisce tutto il necessario affinché i giocatori si connettano con successo. Nel gioco di laser tag di esempio, questo evento corrispondente è Players.PlayerAdded:Connect.

Players.PlayerAdded:Connect è parte di più script nel gioco. Se utilizzi la scorciatoia Ctrl/Cmd+Shift+F e cerchi Players.PlayerAdded:Connect, i risultati forniscono un buon punto di partenza per comprendere la configurazione iniziale del gioco.

Finestra Trova Tutto di Studio con i risultati di Players.PlayerAdded evidenziati.

Per dimostrare, apri ServerScriptServiceSetupHumanoid. La distinzione tra Player e Character è fondamentale per comprendere questo script:

  • Un giocatore è un client connesso, e un personaggio è un modello Humanoid.
  • 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 unito) o non ce l'ha (sta respawnando). Dopo averne trovato uno, chiama onCharacterAdded(), ottiene il modello Humanoid dal personaggio e lo passa a ServerScriptServiceSetupHumanoidsetupHumanoidAsync per la personalizzazione. Dopo aver impostato questi valori, lo script attende che la salute del personaggio raggiunga zero. Imparerai di più sul respawn più avanti in questa sezione del tutorial.

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

La nota importante con questo script è che le proprietà sono completamente opzionali, il che significa che se rimuovi le prime sei righe della funzione, il gioco funziona comunque correttamente. Piuttosto che essere requisiti funzionali, ogni proprietà ti consente di prendere decisioni di design che soddisfano i tuoi obiettivi di gameplay. Ad esempio:

  • Se desideri che i nomi dei personaggi vengano visualizzati a distanze più ravvicinate, riduci il valore di Humanoid.NameDisplayDistance.
  • Se desideri che la salute di un personaggio venga visualizzata solo se è sotto il 100%, imposta Humanoid.HealthDisplayType su DisplayWhenDamaged.
  • Se desideri che i personaggi si distruggano quando la loro salute raggiunge 0, imposta Humanoid.BreakJointsOnDeath su True.

Se cambi i valori di queste proprietà, è importante testare il gioco in modo da poter vedere l'impatto delle tue nuove impostazioni. Puoi ricreare ciò che i giocatori sperimentano in una simulazione multi-client selezionando almeno due personaggi in un test di gioco Server & Clients dal mezzanino.

Opzione Server & Clients nel menu a discesa delle modalità di test del mezzanino di Studio.

Un altro esempio dell'evento Players.PlayerAdded:Connect si trova in ServerScriptServicePlayerStateHandler. Proprio come nell'esempio precedente, PlayerStateHandler controlla immediatamente la presenza di un personaggio. Se il giocatore non è nella lobby, lo script imposta un attributo del giocatore nello stato SelectingBlaster, lo stato iniziale per un turno in cui i giocatori possono selezionare uno dei due diversi tipi di blaster dopo essere spawnati nell'arena. Questo stato include anche un campo di forza che impedisce ai giocatori di subire danni mentre stanno facendo la loro selezione.

PlayerStateHandler
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 particolare variabile in PlayerStateHandler merita una discussione: attributeChangedConnectionByPlayer. Questa tabella memorizza tutti i giocatori e le loro Connections al GetAttributeChangedSignal. Il motivo per cui si memorizza questa connessione in una tabella è che PlayerStateHandler può disconnetterla quando il giocatore lascia il gioco. Questo processo funge da sorta di gestione della memoria per prevenire che il numero di connessioni cresca sempre di più nel tempo.

PlayerStateHandler
local attributeChangedConnectionByPlayer = {}
local function onPlayerAdded(player: Player)
-- Gestisci tutti i futuri aggiornamenti allo 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 di cambiamento dell'attributo quando il giocatore lascia
local function onPlayerRemoving(player: Player)
if attributeChangedConnectionByPlayer[player] then
attributeChangedConnectionByPlayer[player]:Disconnect()
attributeChangedConnectionByPlayer[player] = nil
end
end

Puoi vedere che entrambe le funzioni collegate in onPlayerAdded() chiamano onPlayerStateChanged(). Durante la configurazione iniziale dopo che un giocatore è stato assegnato a un team, onPlayerAdded() imposta PlayerState su SelectingBlaster, quindi la prima istruzione if valuta a false e disabilita il BlasterState. Nella successiva sezione Implementa blaster del tutorial, imparerai maggiori dettagli su questo processo.

PlayerStateHandler
local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- Lo stato del blaster è 'Pronto' solo se lo stato del giocatore è 'In Gioco'
local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled
-- Pianifica la logica di distruzione del campo di forza quando il giocatore inizia a giocare
if newPlayerState == PlayerState.Playing then
scheduleDestroyForceField(player)
end
player:SetAttribute(PlayerAttribute.blasterStateServer, newBlasterState)
end

Se aggiungi dei breakpoint o anche solo una dichiarazione print(), puoi vedere che onPlayerStateChanged() viene chiamato frequentemente durante il gioco: ad esempio, durante la configurazione iniziale di un turno, per impostarsi sul percorso principale del codice, dopo che il giocatore sceglie un blaster e quando il giocatore torna alla lobby, o alla posizione di spawn Neutral. Inoltre, dopo che il giocatore sceglie un blaster, ServerScriptServiceBlasterSelectedHandler imposta il PlayerState su Playing, e PlayerStateHandler può finalmente rimuovere il campo di forza chiamando scheduleDestroyForceField().

Personalizza i campi di forza

Invece di utilizzare un'implementazione personalizzata, il gioco di laser tag di esempio utilizza la classe integrata ForceField di Studio 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 sia includere posizioni di spawn con una proprietà SpawnLocation.Duration maggiore di 0. L'esempio utilizza un valore arbitrario di 9.999 per abilitare i campi di forza, quindi gestisce la durata effettiva programmaticamente in ReplicatedStorageForceFieldClientVisuals.

Simile a setupHumanoidAsync, la maggior parte delle righe in ForceFieldClientVisuals sono opzionali. Ad esempio, se commenti il contenuto della funzione come fa il seguente script, il gioco utilizza il campo di forza scintillante predefinito invece dello script esagonale in StarterGuiForceFieldGui.

Commenting Out Properties in ForceFieldClientVisuals
local function onCharacterAddedAsync(character: Model)
-- local forceField = character:WaitForChild("ForceField", 3)
-- if not forceField then
-- return
-- end
-- forceField.Visible = false
-- localPlayer.PlayerGui:WaitForChild("ForceFieldGui").Enabled = true
-- forceField.Destroying:Wait()
-- localPlayer.PlayerGui.ForceFieldGui.Enabled = false
end

Poiché il campo di forza personalizzato è un'interfaccia grafica piuttosto che un nuovo ParticleEmitter, lo script ForceFieldClientVisuals influisce solo sulle visualizzazioni in prima persona per ciascun giocatore, non sulle visualizzazioni in terza persona quando i giocatori guardano altri giocatori. Le visualizzazioni in terza persona mantengono l'aspetto predefinito di Roblox. Per ulteriori informazioni sulla modifica dei campi di forza, vedere ForceField.Visible.

Le visualizzazioni del campo di forza in prima persona includono una griglia esagonale futuristica sul perimetro dello schermo.
Visualizzazioni del campo di forza in prima persona
Le visualizzazioni del campo di forza in terza persona includono un orbe scintillante blu attorno al giocatore che sta spawnando nel gioco.
Visualizzazioni del campo di forza in terza persona

I campi di forza sono utili perché forniscono ai giocatori abbastanza tempo tra lo spawn e il respawn senza doversi preoccupare dei giocatori nemici, ma alla fine devono scomparire per il gameplay principale del laser tag. Lo script che gestisce la rimozione del campo di forza si trova in ReplicatedStoragescheduleDestroyForceField, e controlla tre condizioni uniche:

  • Dopo che i giocatori selezionano un blaster, i campi di forza devono durare abbastanza a lungo per consentire ai giocatori di acclimatarsi all'ambiente circostante.
  • Durante questo tempo di acclimatazione, i campi di forza non possono essere un vantaggio, quindi devono scomparire nel momento in cui un giocatore spara il proprio blaster.
  • I campi di forza devono scomparire quando i giocatori ripristinano i loro personaggi sia prima di sparare che prima che il campo di forza scada.

Ognuna di queste verifiche nello script scheduleDestroyForceField chiama endForceField() per queste condizioni.

scheduleDestroyForceField
-- Termina il campo di forza se il giocatore spara
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 resetta
characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField)
-- Termina il campo di forza dopo 8 secondi
task.delay(MAX_FORCE_FIELD_TIME, endForceField)

endForceField() include una dichiarazione if apparentemente strana attorno al booleano forceFieldEnded. Poiché i controlli vengono eseguiti in sequenza, lo script può chiamare la funzione endForceField() due o anche tre volte. Il booleano forceFieldEnded 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 ServerScriptServicePlayerStateHandler, c'è un altro script con lo stesso nome in ReplicatedStorage. Il motivo per la divisione è l'architettura client-server:

  • Il client deve comprendere le informazioni sullo stato del giocatore in modo da poter rispondere in modo appropriato in tempo reale, come visualizzare i giusti elementi dell'interfaccia utente o consentire ai giocatori di muoversi e sparare.

  • Il server ha bisogno di tutte queste stesse informazioni in modo da poter prevenire exploit. Ad esempio, il server ha anche bisogno dello stato del giocatore per eseguire azioni come spawnare e equipaggiare personaggi, disabilitare campi di forza e visualizzare una classifica. Questo è il motivo per cui questo script si trova in ReplicatedStorage e non in una posizione puramente client-side.

Per vedere questa logica fondamentale, rivedi il seguente script in ReplicatedStoragePlayerStateHandler che verifica lo stato attuale dell'utente, quindi chiama la funzione appropriata che gestisce le azioni corrispondenti per quello stato.

PlayerStateHandler
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(`Stato del giocatore non valido ({newPlayerState})`)
end
end

Tutte le risposte agli eventi sono logicamente raggruppate in questo script perché richiedono un comportamento simile di abilitazione o disabilitazione dei controlli del giocatore, del movimento della telecamera e di quale livello dell'interfaccia utente è visibile. Ad esempio, durante la selezione del blaster, i giocatori devono essere sia invulnerabili che incapaci di muoversi. 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 stanno selezionando un blaster.

PlayerStateHandler
local function onSelectingBlaster()
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end

La funzione onPlaying() è altrettanto semplice. Abilita il movimento, passa alla visualizzazione principale (HUD), abilita il blaster e chiama la stessa funzione del campo di forza del server.

PlayerStateHandler
local function onPlaying()
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
scheduleDestroyForceField()
end

Respawn dei personaggi

Il gioco di laser tag di esempio gestisce il respawn dei personaggi in un turno attraverso lo stato onTaggedOut() in ReplicatedStoragePlayerStateHandler. Proprio come gli stati onSelectingBlaster() e onPlaying(), onTaggedOut() attiva un comportamento unico in base ai cambiamenti dell'attributo playerState. In particolare, disabilita il movimento del giocatore, presenta l'interfaccia utente di respawn e disabilita il blaster.

PlayerStateHandler
local function onTaggedOut()
-- Disabilita i controlli mentre è taggato fuori
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- Disabilita il blaster mentre è taggato fuori
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end

Se desideri testare questo comportamento, puoi premere Esc, navigare alla scheda Impostazioni, quindi fare clic sul pulsante Ripristina personaggio. Nota che quando attivi lo schermo di respawn, non puoi muoverti, ruotare la telecamera o sparare il tuo blaster.

Menu delle impostazioni di Roblox con il pulsante Ripristina personaggio evidenziato.
Pulsante Ripristina personaggio
Lo schermo di respawn viene visualizzato mentre un giocatore respawna nel turno.
Schermo di respawn

È importante notare che questo script non respawna effettivamente i personaggi, ma semplicemente impedisce loro di agire e fornisce un feedback visivo ai giocatori che il server sta respawnando i loro personaggi. Per dimostrare, se esamini ServerScriptServiceSetupHumanoidsetupHumanoidAsynconHumanoidDied, lo script imposta PlayerState su TaggedOut (essenzialmente notificando ReplicatedStoragePlayerStateHandler), e aggiunge alcuni indicatori visivi. La logica effettiva del respawn è un comportamento integrato di Roblox.

Quando i giocatori respawnano nel turno, respawnano nella posizione di spawn del proprio team in base alla proprietà SpawnLocation.TeamColor. Per personalizzare il tempo di respawn, puoi aggiungere la seguente riga all'inizio di SetupHumanoid. Per saperne di più su questa tecnica, vedere Players.RespawnTime.

SetupHumanoid
local Players = game:GetService("Players")
Players.RespawnTime = 10 -- nuova riga, in secondi

Configurazione varia

Come parte della configurazione iniziale, il gioco di laser tag di esempio esegue anche alcuni piccoli, ma critici passaggi:

  • Il gioco include uno script vuoto chiamato StarterPlayerStarterCharacterScriptsHealth che disabilita la rigenerazione della salute predefinita di Roblox. Per una spiegazione del comportamento di questa proprietà, vedere Humanoid.Health.

  • Il gioco utilizza una telecamera in prima persona impostando la proprietà StarterPlayer.CameraMode.LockFirstPerson. Nota che se desideri consentire agli utenti di passare tra telecamere in prima e terza persona, devi cambiare la proprietà programmaticamente piuttosto che impostarla una sola volta in Studio, e modificare i controlli e l'interfaccia utente per compensare il cambiamento di prospettiva.

  • Il gioco utilizza la classifica integrata di Roblox con l'unità di "punti", che i giocatori guadagnano ogni volta che taggano un altro giocatore. Puoi vedere la configurazione in ServerScriptServiceSetupLeaderboard, ma Classifiche in gioco offre una panoramica completa. Nota che onPlayerTagged aggiunge punti alla classifica, di cui imparerai in Aggiungi turni e Rileva colpi.

Ora che i giocatori possono spawnare, scegliere un blaster e mirarlo da un punto di vista in prima persona, la prossima sezione ti insegnerà gli script dietro la creazione di un gameplay basato su turni.

© 2026 Roblox Corporation. Roblox, il logo Roblox e Powering Imagination sono tra i nostri marchi registrati e non registrati negli Stati Uniti. e altri paesi.