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 back into un'esperienza dopo che hanno raggiunto una condizione di rimozione, come la salute di un personaggio che raggiunge zero o cade dalla mappa. Entrambi i processi sono importanti because they ensure players are able to join your experience, and can continue playing to improve their skills.
Usando l'esperienza laser tag di esempio come riferimento, questa sezione del tutorial ti insegna come usare e personalizzare le funzionalità di Roblox built-in per gestire la generazione e la rigenerazione, tra cui la guida dello scripting su:
- Configurazione delle posizioni di spawn in modo che teamgiocatori possono spawnare solo nella loro zona di spawn.
- Aggiungere nuovi giocatori e i loro personaggi alla partita mentre si uniscono all'esperienza.
- Personalizzare i campi di forza che impediscono ai giocatori di spawnare e respawnare.
- Gestire lo stato del client in modo che il gameplay funzioni correttamente al momento opportuno.
- 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 personaggio.
Questa sezione include molti contenuti di script, ma invece di scrivere tutto da zero quando crei un'esperienza, ti incoraggia a sfruttare i componenti esistenti, ad itere rapidamente e a capire quali sistemi hanno bisogno di un'implementazione personalizzata per corrispondere alla tua visione. Dopo aver completato questa sezione, imparerai a implementare la giochi round based che traccia i punti, monitora lo stato del giocatore e visualizza i risultati round.
Configura le posizioni di spawn
Se giochi l'esperienza in questo momento, tutti i giocatori appariranno casualmente all'oggetto SpawnLocation o all'oggetto SpawnLocation nella zona di spawn del team verde o nella zona di spawn del team rosa. Ciò 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 del giocatore viene rimosso.
Per combattere questo problema, l'esperienza del laser tag di campionamento configura entrambe le posizioni di spawn con un Neutral proprietà impostata su false per limitare i giocatori dell'avversario a spawnare nella zona di spawn errata e un <
- TeamBSpawn – La posizione di spawn nel team rosa con una proprietà TeamColor impostata su Carnation Pink .
Quando un giocatore si unisce all'esperienza, ServerScriptService > Gameplay > Rounds > 1> Rounds1> > 4> spawnPlayersInMap4> controlla per vedere quante persone sono già in ciascuna team, poi restituisce la squadra con il minor numero di persone.
Genera giocatori nella mappa
local function getSmallestTeam(): Team
local teams = Teams:GetTeams()
-- Classifica le squadre in ordine crescente da più piccola a 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 conosciuta la squadra con il minor numero di giocatori, sortisce il giocatore in quella team, imposta la loro proprietà Player.Neutral a false in modo che il giocatore possa generare e respawnare solo alla posizione di spawn della loro team, quindi imposta il loro PlayerState su 1> SelectingBlaster1> , che imparerai di più
Genera giocatori nella 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 esami Workspace > World > Map > 1> Spawns1> , puoi vedere che c'è un'altra posizione di spawn nella esperienza:
Ad esempio, se la partita è attiva, la proprietà Neutral imposta su false in modo che spawnPlayersInMap poss
Per dimostrare, se esami ServerScriptService > Gameplay > Rounds > 1>SpawnPlayersInLobby1> , che viene eseguito alla fine di un round, puoi vedere che per ogni giocatore che viene passato nella tabella 4> players: Player 4>, lo script:
- Imposta la loro proprietà Player.Neutral su true per ripristinare automaticamente il loro Player.Team a nil, consentendo al giocatore di respawnare nella lobby quando un round non è attivo, poiché la posizione di spawn è anche impostata su Neutral .
- Cambia il loro PlayerState in InLobby per rimuovere le immagini del blaster del Giocatoree le prime visualizzazioni dell'interfaccia utente.
Per ulteriori informazioni sulla zona di spawn neutral e sulla sua funzionalità per ciascuna partita, vedi Aggiungere round nella prossima sezione 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 basato sugli eventi, il che significa che gli script ascoltano gli eventi da un servizio Roblox e poi chiamano una funzione in risposta. Ad esempio, quando si aggiungono nuovi giocatori a un'esperienza multiplayer, deve esserci un evento che gestisce tutto ciò di cui i giocatori si connettono con successo. Nell'esempio di etichetta laser, questo evento corrispondente è Players.PlayerAdded:Connect .
Players.PlayerAdded:Connect è una parte di più script nell'esperienza. Se utilizzi la scorciatoia di Ctrl/Commands+Shift+F e cerca Players.PlayerAdded:Connect, i risultati forniscono un buon punto di partenza per capire l'impostazione iniziale dell'esperienza.
Per dimostrare, apri ServerScriptService > SetupHumanoid . La distinzione tra Player e 1> Class.Player.Character|Character1> è la chiave per capire 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 joinato) o no (respawnando). Dopo aver trovato uno, chiama onCharacterAdded(), ottiene il 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 comunque. Invece di 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 si visualizzino a distanze più ravvicinate, riduci il valore di Humanoid.NameDisplayDistance .
- Se vuoi solo la salute di un personaggio visualizzata se è inferiore al 100%, imposta Humanoid.HealthDisplayType su DisplayWhenDamaged .
- Se vuoi che i personaggi si rompano quando la loro salute raggiunge 0, imposta Humanoid.BreakJointsOnDeath su Verità .
Se cambi i valori di queste proprietà, è importante testare i giocatori in un ambiente multiplayer selezionando almeno due personaggi nella sezione Clienti e server della scheda Test.
Un altro esempio dell'evento Players.PlayerAdded:Connect è in ServerScriptService > PlayerStateHandler . Come nel precedente esempio, 1> PlayerStateHandler1> controlla immediat
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 singola variabile in PlayerStateHandler richiede discussione: attributeChangedConnectionByPlayer . Questa tabella memorizza tutti i giocatori e le loro Connections al 1>
PlayerStateHandler
local attributeChangedConnectionByPlayer = {}
local function onPlayerAdded(player: Player)
-- Handling all future updates to player state
attributeChangedConnectionByPlayer[player] = player
:GetAttributeChangedSignal(PlayerAttribute.playerState)
:Connect(function()
local newPlayerState = player:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(player, newPlayerState)
end)
end
-- Disconetti dalla connessione cambiata 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 connesse in onPlayerAdded() chiamata onPlayerStateChanged() . Durante l'Tutorialeiniziale dopo che un giocatore si unisce a una team, <
PlayerStateHandler
local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- Lo stato del Blaster è 'Ready' solo se lo stato del giocatore è 'Playing'
local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled
-- Programma la logica del campo di forza di distruzione 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 print()
Personalizza i Campi di Forza
Invece di utilizzare un'implementazione personalizzata, l'esperienza del laser tag di esempio utilizza la classe ForceField di Studio per impedire ai giocatori di subire danni mentre selezionano il loro laser. Ciò garantisce che l'unico requisito per i
Simile a setupHumanoidAsync , la maggior parte delle righe in ForceFieldClientVisuals sono opzionali. Ad esempio, se commenti i contenuti della funzione come segue lo script, l'esperienza utilizza il campo di forza di base invece del campo di forza esagonale in StarterGui > 1>ForceFieldGui1> .
Commentare le proprietà in ForceFieldClientVisuals
local function onCharacterAddedAsync(character: Model)
-- ForceField = carattere:WaitForChild("ForceField", 3)
-- Se non forceField allora
-- riportare
-- terminare
-- forceField.Visible = false
-- localPlayer.PlayerGui:WaitForChild("ForceFieldGui").Enabled = true
-- forceField.Destroying:Aspetta()
-- localPlayer.PlayerGui.ForceFieldGui.Enabled = false
end
Poiché il campo di forza personalizzato non è un GUI invece di un nuovo ParticleEmitter , lo script ForceFieldClientVisuals influenza solo i primi visual del Giocatore, non i visual del terzo giocatore quando i giocatori guardano ad altri giocatori. I visual del terzo giocatore conservano l'aspetto predefinito di Roblox. Per ulteriori informazioni sulla modifica dei
I campi di forza sono utili perché forniscono ai giocatori abbastanza tempo per passare da un spawn all'altro senza dover preoccuparsi dei giocatori nemici, ma alla fine devono scomparire per il principale Partitadi laser tag. Lo script che gestisce la rimozione dei campi di forza è in ReplicatedStorage > scheduleDestroyForceField , 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 abituarsi ai loro ambienti.
- Durante questo periodo di adattamento, i campi di forza non possono essere un vantaggio, quindi devono scomparire nel momento in cui un giocatore fa esplodere il loro laser.
- I campi di forza devono scomparire quando i giocatori resettano i loro personaggi prima di esplodere o prima che il campo di forza finisca.
Ognuno di questi controlli nel scheduleDestroyForceField script call endForceField() per queste condizioni.
scheduleForza di distruzione
-- 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 campo di forza se il giocatore resets
characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField)
-- Campo di forza fine dopo 8 secondi
task.delay(MAX_FORCE_FIELD_TIME, endForceField)
endForceField() include una dichiarazione if che sembra strana attorno al forceFieldEnded boolean. Poiché i check sono eseguiti in sequenza, lo script può chiamare la funzione 0> endForceField0> due o anche tre volte. La funzione endForceField()3> garantisce che la funzione non distrugga
scheduleForza di distruzione
local function endForceField()
if forceFieldEnded then
return
end
forceFieldEnded = true
attributeChangedConnection:Disconnect()
characterRespawnedConnection:Disconnect()
destroyForceField(player)
end
Handling stato client
Mentre la maggior parte di questa sezione si concentra su ServerScriptService > PlayerStateHandler , c'è un altro script dello stesso nome in ReplicatedStorage . Il motivo per la divisione è l'architettura client-server:
Il client deve capire le informazioni sullo stato del giocatore in modo che possa rispondere in modo appropriato in tempo reale, come mostrare gli elementi dell'interfaccia utente corrispondenti o abilitare i giocatori a muoversi e esplodere.
Il server ha bisogno di tutte queste 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 disabilitazione dei campi di forza e la visualizzazione di una tabella di classifica. Questo è il motivo per cui questo script è in ReplicatedStorage e non una posizione client-side.
Per visualizzare questa logica del core, controlla lo script seguente in ReplicatedStorage > PlayerStateHandler che verifica lo stato attuale dell'utente, poi chiama la funzione appropriata che gestisce le azioni corrispondenti per quel 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(`Invalid player state ({newPlayerState})`)
end
end
Tutte le risposte agli eventi sono logicamente gruppoate insieme in questo script poiché richiedono comportamenti diversi per l'abilitazione o la disabilitazione dei Controllidel giocatore, la movimentazione della telecamera e quale UI layer è 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
PlayerStateHandler
local function onSelectingBlaster()
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
La funzione onPlaying() è simile. Consente movimento, transizioni alla schermata principale (HUD), abilita il blaster e chiama la stessa funzione di campo di forza come il Server.
PlayerStateHandler
local function onPlaying()
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
scheduleDestroyForceField()
end
Rigenerare i personaggi
L'esperienza di laser tag di campionamento gestisce il respawning character back into a round through the onTaggedOut() stato in ReplicatedStorage > PlayerStateHandler . Come la 1> onSelectingBlaster()
PlayerStateHandler
local function onTaggedOut()
-- Disabilita i controlli mentre sei disattivato
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- Disabilita il laser mentre sei etichettato fuori
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end
Se vuoi testare questo comportamento, puoi premere Esc, navigare alla scheda Impostazioni e quindi fare clic sul pulsante Ripristina Personaggio . Nota che quando attivi la schermata di respawn, non puoi Sposta, ruotare la Telecamerao eseguire il tuo blaster.
È importante notare che questo script non respawna effettivamente i personaggi, li ferma da agire e fornisce feedback visivi
Quando i giocatori respawn back into the round, they respawn at their team's spawn location according to the SpawnLocation.TeamColor Proprietà. To customize the respawn time, you can add the following line to the top of SetupHumanoid . To learn more about this technique, see Players.RespawnTime .
SetupUmanoid
local Players = game:GetService("Players")Players.RespawnTime = 10 -- new line, in seconds
Configurazione varia
Come parte dell'iniziale configurazione, l'esperienza del laser di campionamento esegue anche alcuni piccoli, ma critici passi:
L'esperienza include uno script vuoto chiamato StarterPlayer > StarterCharacterScripts > Health che disabilita la rigenerazione della salute predefinita di Roblox. Per un'esplanazione di questo comportamento della Proprietà, vedi 1> Class.Humanoid.Health1> .
L'esperienza utilizza una prima persona camera impostando la ProprietàStarterPlayer.CameraMode.LockFirstPerson . Nota che se si desidera consentire agli utenti di cambiare tra prima e terza persona camera, è necessario cambiare il programma di proprietà in modo da non impostare solo una volta in Studio, e modificare i controlli e l'interfaccia utente per compensare per il cambiamento di prospettiva.
L'esperienza utilizza la ClassificaRoblox incorporata con l'unità di "punti", che i giocatori guadagno ogni volta che etichettano un altro giocatore. Puoi vedere la configurazione in ServerScriptService > SetupLeaderboard , ma In-Experience Leaderboards
Ora che i giocatori possono Rigenerare, scegli un blaster e miralo da una prospettiva di prima persona, la prossima sezione ti insegna gli script dietro la creazione di Partitabasati sul round.