Implementare il comportamento del blaster è il processo di programmazione di una meccanica di esplosione nelle esperienze di sparatutto in prima persona. Mentre i giocatori possono esplodere con un solo clic o pressione di un pulsante, creare un comportamento di esplosione soddisfacente e preciso è importante perché migliora il divertimento dei giocatori durante il gameplay complessivo.
Utilizzando l'esperienza di laser tag di esempio come riferimento, questa sezione del tutorial ti insegna gli script dietro l'implementazione del comportamento del blaster per due diversi tipi di blaster, inclusa la guida su:
- Rilevare quando i giocatori premendo il pulsante di esplosione.
- Verificare se il giocatore può utilizzare il proprio blaster se ha recentemente premuto il pulsante di esplosione.
- Generare dati di esplosione che indicano al server chi ha avviato l'esplosione, da dove è venuta e qual è stata la destinazione finale di ogni raggio laser.
- Notificare il server dei dati di esplosione affinché possa svolgere le azioni appropriate se l'esplosione è collisa con un altro giocatore.
- Ripristinare il blaster tra ogni esplosione per dare al blaster il tempo sufficiente per raffreddarsi prima di poter esplodere di nuovo.
Dopo aver completato questa sezione, imparerai a conoscere gli script che consentono al blaster di rilevare quando le sue esplosioni collidono con altri giocatori, quindi dedurre l'importo corrispondente di salute in base a ciascun tipo di blaster.
Rileva l'input del giocatore
Il primo passo per implementare il comportamento del blaster è ascoltare quando un giocatore preme il pulsante di esplosione. Il tipo di input che i giocatori utilizzano per premere il pulsante di esplosione dipende dal dispositivo che stanno utilizzando per accedere all'esperienza. Ad esempio, l'esperienza di laser tag di esempio supporta controlli con mouse e tastiera, gamepad e controlli touch. Puoi vedere ciascuno di questi tipi di input in ReplicatedStorage ⟩ UserInputHandler.
Questo script client utilizza ContextActionService per collegare MouseButton1 e ButtonR2 all'azione di esplosione. Ciò significa che ogni volta che un giocatore preme il pulsante sinistro del mouse o il pulsante R2 di un gamepad, viene attivato un raggio laser che esplode dal blaster. Nota che il HUDGui contiene un pulsante per esplodere sui dispositivi mobili, che viene collegato successivamente nello script.
UserInputHandlerContextActionService:BindAction("_", onBlasterActivated, false,Enum.UserInputType.MouseButton1,Enum.KeyCode.ButtonR2)
Un'altra nota importante è l'uso di Enum.UserInputState.Begin nella definizione di onBlasterActivated(). Molte interazioni dell'interfaccia utente, come selezionare un blaster in questo esempio, non si verificano fino a dopo che il pulsante del mouse si alza (Enum.UserInputState.End), dando agli utenti un'ultima possibilità di evitare l'interazione. Tuttavia, una meccanica di esplosione non sembra reattiva a meno che non si verifichi istantaneamente nel momento in cui il pulsante viene premuto.
Per dimostrare, puoi cambiare Enum.UserInputState.Begin in Enum.UserInputState.End, quindi eseguire un test per vedere come la reattività dell'esplosione impatta il gameplay dell'esperienza. Ad esempio, se i giocatori possono tenere premuto il pulsante senza attivare l'esplosione, come potrebbe cambiare la loro esperienza nel colpire altri giocatori?
UserInputHandler
local function onBlasterActivated(_actionName: string,
inputState: Enum.UserInputState, _inputObject: InputObject)
if inputState == Enum.UserInputState.End then -- riga aggiornata, assicurati di cambiare di nuovo
attemptBlastClient()
end
end
Verifica se il giocatore può esplodere
Dopo che UserInputHandler rileva una pressione di un pulsante o un tocco dello schermo, chiama ReplicatedStorage ⟩ Blaster ⟩ attemptBlastClient per verificare se il giocatore può esplodere o meno. Come la maggior parte dei controlli nell'esperienza di laser tag di esempio, si verifica due volte: prima sul client e poi più tardi sul server. attemptBlastClient poi chiama ReplicatedStorage ⟩ Blaster ⟩ canLocalPlayerBlast per eseguire un semplice controllo dell'attributo blasterStateClient del giocatore:
canLocalPlayerBlast
local function canLocalPlayerBlast(): boolean
return localPlayer:GetAttribute(PlayerAttribute.blasterStateClient) == BlasterState.Ready
end
Se esamini ReplicatedStorage ⟩ Blaster ⟩ BlasterState, puoi vedere che l'esperienza ha tre stati del blaster: Ready, Blasting e Disabled. Per vedere l'effetto di ciascuno di questi stati, puoi eseguire il test dell'esperienza, selezionare il tuo giocatore sotto il servizio Players, quindi osservare l'attributo blasterStateClient nella finestra Properties. Nota come visualizza Disabled mentre scegli il tuo blaster, Ready per la maggior parte del tempo e Blasting per meno di un secondo dopo aver premuto il pulsante.
Questa leggera pausa ti impedisce di esplodere così rapidamente come riesci a cliccare. Ad esempio, se cambi la funzione per restituire sempre true, puoi esplodere rapidamente il tuo blaster senza alcun ritardo, il che è irrealistico per il gameplay del laser tag.
canLocalPlayerBlast
local function canLocalPlayerBlast(): boolean
return true -- riga aggiornata, assicurati di cambiare di nuovo
end
Genera dati di esplosione
Dopo aver verificato che il blaster del giocatore è nello stato Ready, attemptBlastClient chiama ReplicatedStorage ⟩ attemptBlastClient ⟩ blastClient. Il primo passo che blastClient compie è impostare l'attributo del giocatore blasterStateClient su Blasting, il che evita il caso di fuoco rapido precedentemente menzionato.
Il passo successivo è generare i dati di esplosione. Se esamini ReplicatedStorage ⟩ Blaster ⟩ BlastData, puoi vedere che ogni esplosione consiste in tre pezzi di informazioni:
- Il giocatore che avvia l'esplosione.
- Un DataType.CFrame che rappresenta il punto di origine dell'esplosione.
- Una tabella RayResult che contiene la destinazione finale di ogni raggio laser e il giocatore colpito, se ha colpito un altro giocatore.
Per generare questi dati, blastClient chiama ReplicatedStorage ⟩ attemptBlastClient ⟩ blastClient ⟩ generateBlastData, che puoi rivedere qui sotto.
generateBlastData
local function generateBlastData(): BlastData.Type
local blasterConfig = getBlasterConfig()
local rayDirections = getDirectionsForBlast(
currentCamera.CFrame, blasterConfig)
local rayResults = castLaserRay(
localPlayer, currentCamera.CFrame.Position, rayDirections)
local blastData: BlastData.Type = {
player = localPlayer,
originCFrame = currentCamera.CFrame,
rayResults = rayResults,
}
return blastData
end
Questa funzione inizia utilizzando getBlasterConfig per recuperare il tipo di blaster del giocatore. L'esempio fornisce due tipi di blaster: uno che produce diversi fasci con una diffusione orizzontale ampia e un altro che produce un singolo fascio. Puoi trovare le loro configurazioni in ReplicatedStorage ⟩ Instances ⟩ LaserBlastersFolder.
La funzione usa poi currentCamera.CFrame come punto di origine per l'esplosione, passandolo a getDirectionsForBlast. A questo punto, il codice non riguarda più il blaster, ma il raggio laser, di cui imparerai di più nella sezione rileva colpi del tutorial. Infine, dopo aver creato la tabella rayResults, generateBlastData ha tutte le informazioni necessarie per restituire i dati di esplosione a blastClient.
Notifica il server
Una volta che blastClient ha i dati completi per l'esplosione, attiva due eventi:
blastClientlocal laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEventlocal laserBlastedEvent = ReplicatedStorage.Instances.LaserBlastedEventlaserBlastedBindableEvent:Fire(blastData)laserBlastedEvent:FireServer(blastData)
Il BindableEvent notifica altri script client dell'esplosione. Ad esempio, ReplicatedStorage ⟩ FirstPersonBlasterVisuals utilizza questo evento per sapere quando visualizzare effetti visivi, come l'animazione dell'esplosione e la barra di raffreddamento. Allo stesso modo, il RemoteEvent notifica gli script server dell'esplosione, che inizia a elaborare l'esplosione in ServerScriptService ⟩ LaserBlastHandler.
LaserBlastHandler
local function onLaserBlastedEvent(playerBlasted: Player, blastData: BlastData.Type)
local validatedBlastData = getValidatedBlastData(playerBlasted, blastData)
if not validatedBlastData then
return
end
if not canPlayerBlast(playerBlasted) then
return
end
blastServer(playerBlasted)
processTaggedPlayers(playerBlasted, blastData)
for _, replicateToPlayer in Players:GetPlayers() do
if playerBlasted == replicateToPlayer then
continue
end
replicateBlastEvent:FireClient(replicateToPlayer, playerBlasted, blastData)
end
end
Per aiutare a prevenire imbroglio, il server deve verificare tutti i dati che ciascun client invia. Questi controlli includono:
- È BlastData una tabella? Contiene un Class.CFrame e un'altra tabella chiamata rayResults?
- Il giocatore ha un blaster equipaggiato?
- Il giocatore ha un personaggio e una posizione all'interno del mondo?
- Dopo aver inviato i dati di esplosione, il giocatore si è spostato a una distanza eccessiva da dove ha esploso il raggio laser?
Quest'ultimo controllo comporta una valutazione da parte del giudice e, secondo la latenza del server e la velocità di movimento del giocatore, potresti decidere che diversi valori sono eccessivi per la tua esperienza. Per dimostrare come fare questa valutazione, puoi farti un'idea della magnitudine tipica del cambiamento posizionale aggiungendo un'istruzione di stampa in getValidatedBlastData e testando l'esperienza.
getValidatedBlastDatalocal distanceFromCharacterToOrigin = blastData.originCFrame.Position - rootPartCFrame.Positionprint(distanceFromCharacterToOrigin.Magnitude) -- riga aggiornata, assicurati di rimuovereif distanceFromCharacterToOrigin.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS thenwarn(`Player {player.Name} failed an origin sanity check while blasting`)returnend
Mentre ti muovi e esplodi, prendi nota dell'output. Potrebbe assomigliare a questo:
1.90196299552917483.15495586395263672.57428836822509774.80445861816406252.6434271335601807
Se aumenti la velocità di movimento per i giocatori in ReplicatedStorage ⟩ PlayerStateHandler ⟩ togglePlayerMovement, quindi esegui di nuovo il test, è probabile che tu incontri molti controlli falliti a causa di movimenti eccessivi tra le esplosioni.
togglePlayerMovementlocal ENABLED_WALK_SPEED = 60 -- riga aggiornata, assicurati di cambiare di nuovo
Il server quindi esegue le seguenti operazioni:
- Valida rayResults.
- Controlla se il giocatore può esplodere.
- Ripristina lo stato del blaster.
- Riduce la salute per qualsiasi giocatore contrassegnato.
- Replica l'esplosione a tutti gli altri giocatori affinché possano vedere effetti visivi in terza persona.
Per ulteriori informazioni su queste operazioni del server, vedere la sezione rileva colpi del tutorial.
Ripristina il blaster
Nell'esperienza di laser tag di esempio, i blaster utilizzano una meccanica di calore. Anziché ricaricare dopo un numero impostato di esplosioni, hanno bisogno di tempo per "raffreddarsi" tra ciascuna esplosione. Questo stesso ritardo di cooldown si verifica sia sul client (blastClient) che sul server (blastServer), con il server che funge da fonte di verità.
blastServer
local blasterConfig = getBlasterConfig(player)
local secondsBetweenBlasts = blasterConfig:GetAttribute("secondsBetweenBlasts")
task.delay(secondsBetweenBlasts, function()
local currentState = player:GetAttribute(PlayerAttribute.blasterStateServer)
if currentState == BlasterState.Blasting then
player:SetAttribute(PlayerAttribute.blasterStateServer, BlasterState.Ready)
end
end)
L'attributo secondsBetweenBlasts fa parte della configurazione del blaster in ReplicatedStorage ⟩ Instances ⟩ LaserBlastersFolder. Dopo che il ritardo di secondsBetweenBlasts è trascorso, il giocatore può esplodere di nuovo e l'intero processo si ripete. Per aiutare il giocatore a capire quando può esplodere di nuovo, l'esperienza include una barra di raffreddamento.
A questo punto, i giocatori possono apparire e riapparire, mirare e esplodere, ma l'esperienza deve ancora determinare i risultati di ciascuna esplosione. Nella prossima sezione del tutorial, imparerai a programmare la capacità del blaster di rilevare quando l'esplosione colpisce un altro giocatore, quindi ridurre l'importo appropriato di salute del giocatore secondo le impostazioni del blaster.