Rilevare Colpi

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

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:

  1. Un controllo fisicamente simulato per vedere se un proiettile ha colpito il bersaglio.
  2. 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 dritto
table.insert(directions, originCFrame.LookVector)
elseif numLasers > 1 then
-- Per più laser, distribuiscili in modo uniforme in orizzontale
-- su un intervallo laserSpreadDegrees intorno al centro
local leftAngleBound = laserSpreadDegrees / 2
local rightAngleBound = -leftAngleBound
local degreeInterval = laserSpreadDegrees / (numLasers - 1)
for angle = rightAngleBound, leftAngleBound, degreeInterval do
local direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVector
table.insert(directions, direction)
end
end

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 then
table.insert(directions, originCFrame.LookVector)
elseif numLasers > 1 then
local leftAngleBound = laserSpreadDegrees / 2
local rightAngleBound = -leftAngleBound
local degreeInterval = laserSpreadDegrees / (numLasers - 1)
for angle = rightAngleBound, leftAngleBound, degreeInterval do
local direction
if spreadDirection == "vertical" then
direction = (originCFrame * CFrame.Angles(math.rad(angle), 0, 0)).LookVector
else
direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVector
end
table.insert(directions, direction)
end
end
return 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 linea
print(direction) -- nuova linea
end -- nuova linea
local 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

A diagram where Ray A continues through the wall, and Ray B collides with the wall.

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

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_DISTANCE
destination = CFrame.lookAt(distantPosition, distantPosition - rayDirection)
taggedPlayer = nil
end

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

  1. 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).

  2. 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:

    isRayAngleFromOriginValido

    local claimedDirection = (rayResult.destination.Position - originCFrame.Position).Unit
    local directionErrorDegrees = getAngleBetweenDirections(claimedDirection, expectedDirection)
    return directionErrorDegrees <= ToleranceValues.BLAST_ANGLE_SANITY_CHECK_TOLERANCE_DEGREES

    RobloxAbstractizza 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:

    ottenereAngleBetweenDirections

    local 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)
    end
  3. Il 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

    èPlayerNearPosition

    local distanceFromCharacterToPosition = position - character:GetPivot().Position
    if distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS then
    return false
    end
  4. L'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

    isRayPathObiettato

    local 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.