Rilevare gli impatti è il processo di identificazione del momento in cui le esplosioni si scontrano con i giocatori, quindi riducendo la loro salute di conseguenza. A livello elevato, puoi pensare a questo lavoro come segue:
- Un controllo fisicamente simulato per vedere se un proiettile ha colpito il bersaglio.
- Un controllo istantaneo se il blaster era puntato al bersaglio.
Il tipo di rilevamento degli impatti che utilizzi dipende dai requisiti di gioco della tua esperienza. Ad esempio, un check fisicamente simulato è appropriato per un'esperienza di dodgeball in cui le palle devono lasciare la mano ad una certa velocità, cadere mentre si muove attraverso l'aria o cambiare direzione dalle condizioni meteorologiche. Tuttavia, un check istantaneo è un match migliore per un'esperienza di laser tag in cui i raggi dev
Usando la esperienza di tag laser di esempio come riferimento, questa sezione del tutorial ti insegna gli script dietro la rilevazione dei colpi nello Spazio3D, tra cui la guida su:
- Ottenere la direzione dell'esplosione dai valori della telecamera attuale e dal inserisci / scrividi blaster del Giocatore.
- Lanciare i raggi in un percorso dritto dal blaster mentre esplode.
- Validazione dell'esplosione per prevenire l'esplosione dei dati del blaster.
- Riduzione della salute del giocatore in base al danno da esplosione di ciascun tipo di blaster e quante vie ci siano colpite dal Giocatore.
Dopo aver completato questa sezione, puoi esplorare ulteriori argomenti di sviluppo per migliorare la tua esperienza di Partita, come audio/suono, illuminazione e effetti speciali.
Ottieni la direzione di esplosione
Dopo che un giocatore ha esploso il suo laser, ReplicatedStorage > Blaster > tentativoBlastClient > 1> blastClient1> > 4> generateBlastData4> chiama due funzioni per avviare il processo di rilevamento del colpo: 7> rayDirections()7> e 0> rayResults()
GeneraBlastData
local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)
Gli input per rayDirections sono semplici: la posizione e i valori di rotazione della telecamera attuale, e il inserisci / scrividi laser del Giocatore. Se l'esperienza di tag laser di esempio ReplicatedStorage > LaserRay > 1>getDirectionsForBlast1> non fosse necess
Tuttavia, poiché l'esempio fornisce un tipo di blaster aggiuntivo che produce più laser con una larga diffusione orizzontale, getDirectionsForBlast deve calcolare la direzione per ciascun laser della diffusione in base ai loro angoli nella configurazione del blaster:
ottenereIndicazioniPerBlast
if numLasers == 1 then-- Per i singoli laser, punteranno drittotable.insert(directions, originCFrame.LookVector)elseif numLasers > 1 then-- Per più laser, distribuiscili in modo uniforme in orizzontale-- su un intervallo laserSpreadDegrees intorno al centrolocal leftAngleBound = laserSpreadDegrees / 2local rightAngleBound = -leftAngleBoundlocal degreeInterval = laserSpreadDegrees / (numLasers - 1)for angle = rightAngleBound, leftAngleBound, degreeInterval dolocal direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVectortable.insert(directions, direction)endend
Per dimostrare questo concetto ulteriormente, se includessi un terzo tipo di blaster con una larga, 垂直 diffusione, potresti creare un nuovo attributo di blaster, come spreadDirection , quindi regolare il calcolo del CFrame per utilizzare un asse diverso. Ad esempio, nota la differenza nei calcoli di <
if numLasers == 1 thentable.insert(directions, originCFrame.LookVector)elseif numLasers > 1 thenlocal leftAngleBound = laserSpreadDegrees / 2local rightAngleBound = -leftAngleBoundlocal degreeInterval = laserSpreadDegrees / (numLasers - 1)for angle = rightAngleBound, leftAngleBound, degreeInterval dolocal directionif spreadDirection == "vertical" thendirection = (originCFrame * CFrame.Angles(math.rad(angle), 0, 0)).LookVectorelsedirection = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVectorendtable.insert(directions, direction)endendreturn directions
In ultima istanza, la funzione rayDirections() restituisce una tabella di Vectors che rappresenta la direzione di ciascun raggio laser. Se è utile, puoi aggiungere alcuni registri per ottenere un'idea di ciò che questo dato sembra.
GeneraBlastData
local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)for _, direction in rayDirections do -- nuova lineaprint(direction) -- nuova lineaend -- nuova linealocal rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)
Raggi di proiezione
castLaserRay() , la seconda funzione in ReplicatedStorage > Blaster > 0> tentativoBlastClient
Questa informazione è particolarmente utile per le esperienze di sparatore in prima persona poiché ti consente di vedere quando e dove gli esplosioni si intersecano con i giocatori o l'Ambiente. Ad esempio, la seguente immagine mostra due raggi che si intersecano tra loro in parallelo. Secondo il loro punto di origine e direzione, Ray A manca il muro e continua fino a quando non raggiunge la sua distanza massima, mentre Ray B collide con il muro. Per ul
I parametri castLaserRay() specificano che Raycast() chiami devono considerare ogni parte nell'area di lavoro ecetto il personaggio che ha esploso. Il script poi genera un raggio per ogni direzione nella tabella 1> directions1>. Se un raggio colpisce qualcosa, genera un 4>Datatype.RaycastResult4>, che ha
- Distance – La distanza tra l'origine del ray e il punto di intersezione.
- Material – Il Enum.Material all'intersezione.
Il valore Instance è il più critico di queste proprietà per il gameplay del tag laser per l'esempio porque collide quando i raggi si scontrano con altri Giocatore. Per recuperare queste informazioni, l'Ambiente
castLaserRay() quindi usa Position e Normal per creare un nuovo 0> Datatype.CFrame</
castLaserRay
if result then-- L'esplosione ha colpito qualcosa, controlla se era un Giocatore.destination = CFrame.lookAt(result.Position, result.Position + result.Normal)taggedPlayer = getPlayerFromDescendant(result.Instance)else-- L'esplosione non ha colpito nulla, quindi la sua destinazione è-- il punto alla sua distanza massima.local distantPosition = origin + rayDirection * MAX_DISTANCEdestination = CFrame.lookAt(distantPosition, distantPosition - rayDirection)taggedPlayer = nilend
Validare l'esplosione
Per prevenire cheating, il capitolo precedente Impiegando Blaster spiega come blastClient notifica il server del blast using a RemoteEvent so that it can verify all data that
In primo luogo, getValidatedRayResults chiama validateRayResult per controllare che ogni rigo nella tabella rayResults dal client sia un 1> Datatype.CFrame1> e un 4> Player4> (o null).
Successivamente, chiama isRayAngleFromOriginValid per confrontare gli angoli previsti del laser con quelli del client. Questo codice in particolare mostra l'avvantaggio di utilizzare ReplicatedStorage perché il server può chiamare getDirectionsForBlast se stesso, memorizza il ritorno come i dati "aspettati" e poi confronta con i dati dal client.
Come la convalida del blaster dal capitolo precedente, isRayAngleFromOriginValid si basa su un valore di tolleranza per determinare quale costituisce una differenza "eccedente" negli angoli:
isRayAngleFromOriginValidolocal claimedDirection = (rayResult.destination.Position - originCFrame.Position).Unitlocal directionErrorDegrees = getAngleBetweenDirections(claimedDirection, expectedDirection)return directionErrorDegrees <= ToleranceValues.BLAST_ANGLE_SANITY_CHECK_TOLERANCE_DEGREESRobloxAbstractizza i bit più coinvolti della matematica, il che rende il risultato una funzione di aiuto breve e altamente riutilizzabile con applicabilità in una gamma di esperienze:
ottenereAngleBetweenDirectionslocal function getAngleBetweenDirections(directionA: Vector3, directionB: Vector3)local dotProduct = directionA:Dot(directionB)local cosAngle = math.clamp(dotProduct, -1, 1)local angle = math.acos(cosAngle)return math.deg(angle)endIl prossimo controllo è il più intuitivo. Mentre getValidatedBlastData usa DISTANCE_SANITY_CHECK_TOLERANCE_STUDS per verificare che il giocatore che ha sparato era vicino al punto di origine del raggio, isPlayerNearPosition usa la stessa logica per controllare se il giocatore contrassegnato era vicino al punto
èPlayerNearPositionlocal distanceFromCharacterToPosition = position - character:GetPivot().Positionif distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS thenreturn falseendL'ultimo controllo isRayPathObstructed usa una variazione dell'operazione di fusione del ray per controllare se la destinazione del ray è dietro un muro o un'altra obstruzione dalla posizione del client. Ad esempio, se un giocatore malvagio rimuoveva sistematicamente tutte le pareti dall'esperienza per contrassegnare altri giocatori, il server avrebbe controllato e confermato che i ray non sono validi poiché conosce ogni pos
isRayPathObiettatolocal scaledDirection = (rayResult.destination.Position - blastData.originCFrame.Position)scaledDirection *= (scaledDirection.Magnitude - 1) / scaledDirection.Magnitude
Nessuna strategia anti-exploit è completa, ma è importante considerare come i giocatori maliziosi potrebbero avvicinare la tua esperienza in modo da poter mettere in atto controlli che il server può eseguire per segnalare comportamenti sospetti.
Riduci la salute del giocatore
Dopo aver verificato che un giocatore ha contrassegnato un altro Giocatore, gli ultimi passi per completare il main gameplay loop in the sample laser tag experience sono ridurre la salute del Giocatorecontrassegnato, incrementare la Classificae respawn il giocatore back into the round.
Inizialmente riducendo la salute del Giocatorecontrassegnato, Spawning and Respawning copre la distinzione tra Player e Class.
Le esperienze memorizzano i valori dei danni nel damagePerHit attributo di ciascun blaster
Health non accetta valori negativi, quindi onPlayerTagged ha qualche logica per mantenere la salute del giocatore in oltre zero. Dopo aver verificato che la salute del giocatore è in oltre zero, paragrafa la salute a damagePerHit e usa il
Questo modo di avvicinarsi al problema potrebbe sembrare un po 'complicato. Ad esempio, perché non impostare la salute del giocatore a zero se fosse negativo? Il motivo è perché impostare i valori della salute evita il campo di forza. Utilizzando il metodo Humanoid:TakeDamage() , si garantisce che i giocatori non subiscano danni mentre i loro campi di forza sono attivi.
su PlayerTagged
local function onPlayerTagged(playerBlasted: Player, playerTagged: Player, damageAmount: number)
local character = playerTagged.Character
local isFriendly = playerBlasted.Team == playerTagged.Team
-- Disattiva il Lanciareamichevole
if isFriendly then
return
end
local humanoid = character and character:FindFirstChild("Humanoid")
if humanoid and humanoid.Health > 0 then
-- Evita la salute negativa
local damage = math.min(damageAmount, humanoid.Health)
-- TakeDamage garantisce che la salute non sia inferiore se ForceField è attivo
humanoid:TakeDamage(damage)
if humanoid.Health <= 0 then
-- Award playerBlasted a point for tagging playerTagged
Scoring.incrementScore(playerBlasted, 1)
end
end
end
Il prossimo passo è quello di incrementare la Classifica. Potrebbe sembare inutile per LaserBlastHandler includere il giocatore che ha esploso accanto ai dati di esplosione, ma senza quelle informazioni, l'esperienza non può attribuire al giocatore il merito di aver fatto qualcosa per uscire. Infine, il giocatore taggato respawna nel round, che puoi rivedere in Spawning and Respawning .
I cinque capitoli in questo tutorial coprono il flusso di gioco principale dell'esperienza, ma ci sono ancora molte aree da esplorare, come:
- Immagini Blaster visive : Vedi ReplicatedStorage > FirstPersonBlasterVisuals e 0> ServerScriptService0> > 3> ThirdPersonBlasterVisuals 3> .
- Audio : Vedi ReplicatedStorage > SoundHandler .
- Modalità personalizzate : Come potresti modificare questa esperienza per introdurre nuovi tipi di obiettivi, come ottenere il maggior numero di punti prima che il tempo finisca?
Per la logica di gioco estesa per l'esperienza del laser tag, nonché risorse ambientali riutilizzabili e di alta qualità, fai riferimento alla tag laser modello.