Detectando Ataques

*Este conteúdo é traduzido por IA (Beta) e pode conter erros. Para ver a página em inglês, clique aqui.

Detecting hits é o processo de identificar quando explosões colidem com jogadores, então reduzir sua saúde apropriadamente. Em um nível alto, você pode pensar nisso como:

  1. Um cheque fisicamente simulado de se um projétil atingiu o alvo.
  2. Uma verificação instantânea de se o blaster estava apontado para o alvo.

O tipo de detecção de hit que você usa depende dos requisitos de jogoplay da sua experiência. Por exemplo, um cheque fisicamente simulado é apropriado para uma experiência de dodgeball onde as bolas precisam deixar a mão em uma certa velocidade, cair como elas se movem pelo ar ou alterar direção de condições meteorológicas. No entanto, um check instantâneo é melhor para uma experiência de laser tag onde os raios devem ter uma velocidade quase infinita

Usando a experiência de laser de exemplo como referência, esta seção do tutorial ensina você sobre os scripts por trás da detecção de hit no espaço 3D, incluindo orientações sobre:

  • Obtendo a direção de explosão a partir dos valores da câmera atual e do digitarde arma do jogador.
  • Projeta raios em um caminho direto do blaster à medida que ele explode.
  • Validando a explosão para impedir a exploração de dados do blaster.
  • Reduzindo a saúde do jogador de acordo com o dano de explosão de cada tipo de blaster e quantos raios atingem o jogador.

Depois de concluir esta seção, você pode explorar tópicos de desenvolvimento adicionais para melhorar sua jogabilidade, como áudio, iluminação e efeitos especiais.

Obter direção de explosão

Depois que um jogador atira no seu blaster, Armazenamento Replicado > Blaster > tentarBlastClient > 1> blastClient1> > 4> generateBlastData4> chama duas funções para iniciar o processo de detecção de hit: 7> rayDirections()7> e 0> rayResults()

gerarDadosExplosivos

local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)
local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)

As entradas para rayDirections são simples: a posição e valores de rotação atual da câmera, e o digitarde laser do jogador. Se a experiência de rastreamento de laser só fornecer jogadores lasers que produzem um único laser, Armazenamento Replicado > LaserRay >

No entanto, porque a amostra fornece um tipo de blaster adicional que produz vários feixes de laser com uma larga, distribuição horizontal, getDirectionsForBlast deve calcular a direção para cada feixe de laser da distribuição de acordo com seus ângulos dentro da configuração do blaster:

obterDireçõesParaBlast

if numLasers == 1 then
-- Para lasers únicos, eles apontam direto
table.insert(directions, originCFrame.LookVector)
elseif numLasers > 1 then
-- Para múltiplos lasers, espalhe-os uniformemente horizontalmente
-- sobre um intervalo laserSpreadDegrees ao redor do 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

Para demonstrar este conceito ainda mais, se você incluir um terceiro tipo de blaster com uma larga, vertical spread, você pode criar um novo atributo de blaster, como spreadDirection, então ajustar a CFrame cálculo para usar um diferente digitar. Por exemplo, observe a diferença nas cálculos de


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

Em última análise, a função rayDirections() retorna uma tabela de Vectors que representa a direção de cada feixe de laser. Se for útil, você pode adicionar algum log para ter uma visão de como esses dados se parecem.

gerarDadosExplosivos

local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)
for _, direction in rayDirections do -- nova linha
print(direction) -- nova linha
end -- nova linha
local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)

Raios de Drenagem

castLaserRay() , a segunda função em Armazenamento Replicado > Blaster > 0> tentB

Essas informações são particularmente úteis para experiências de primeira pessoa de shooter, pois permitem que você veja quando e onde as explosões interagem com os jogadores ou o ambiente. Por exemplo, a seguinte imagem mostra duas raias que estão se projetando em paralelo umas com as outras. De acordo com seu ponto de origem e direção, a Ray A erra a parede e continua até que ela atinja sua distância máxima, enqu

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

Os parâmetros castLaserRay() especificam que Raycast() chamadas devem considerar cada parte na área de trabalho exceto o personagem que explodiu. O script então cria um raio para cada direção na tabela 2>directions2>. Se um raio atingir algo, ele gera um 5> Datatype.RaycastResult5>, que

O valor Instance é o mais crítico dessas propriedades para a experiência de jogo de laser de raios com outros jogadores. Para recuperar essas informações, a experiência usa a função armazenamento de có

castLaserRay() então usa Position e Normal para criar um novo 0> Datatype.CFrame

castLaserRay

if result then
-- O impacto explodiu algo, verifique se era um jogador.
destination = CFrame.lookAt(result.Position, result.Position + result.Normal)
taggedPlayer = getPlayerFromDescendant(result.Instance)
else
-- A explosão não atingiu nada, então seu destino é
-- o ponto em sua distância máxima.
local distantPosition = origin + rayDirection * MAX_DISTANCE
destination = CFrame.lookAt(distantPosition, distantPosition - rayDirection)
taggedPlayer = nil
end

Validar o Explosão

Para evitar cheating, o capítulo anterior Implementando Blasters explica como blastClient notifica o servidor do blast usando um RemoteEvent para que ele possa verificar todos

  1. Primeiro, getValidatedRayResults chamadas validateRayResult para verificar que cada entrada na tabela rayResults do cliente é um 1> Datatype.CFrame1> e um 4> Player4> (ou nil).

  2. Em seguida, ele chama isRayAngleFromOriginValid para comparar os ângulos esperados do laser ao laser espalhado ao cliente. Este código em particular mostra a vantagem de usar ReplicatedStorage porque o servidor pode chamar getDirectionsForBlast mesmo, armazenar a saída como os dados "esperados" e depois compará-la com os dados do

    Exatamente como a validação de blaster do capítulo anterior, isRayAngleFromOriginValid confia em um valor de tolerância para determinar o que constitui uma diferença "excessiva" em ângulos:

    éRayAngleFromOriginValid

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

    O Roblox abstrai os bits mais envolvidos de matemática, então o resultado é uma função de auxílio curta, altamente reutilizável com aplicabilidade em uma variedade de experiências:

    obterAngleBetweenDirections

    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. A próxima verificação é a mais intuitiva. Embora getValidatedBlastData use DISTANCE_SANITY_CHECK_TOLERANCE_STUDS para verificar se o jogador que explodiu estava perto do ponto de origem do feixe, isPlayerNearPosition usa lógica idêntica para verificar se o jogador marcado estava perto do ponto de destino do fe

    isPlayerNearPosition

    local distanceFromCharacterToPosition = position - character:GetPivot().Position
    if distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS then
    return false
    end
  4. O último check isRayPathObstructed usa uma variação da operação de raycast para verificar se o destino do ray está atrás de uma parede ou outra obstrução da posição do cliente. Por exemplo, se um jogador malicioso remover todas as paredes da experiência para marcar outros jogadores, o servidor verificaria e confirmaria que os raios são inválidos, pois conhece todas as posições de todos os objetos dentro do ambiente.

    isRayPathObstruído

    local scaledDirection = (rayResult.destination.Position - blastData.originCFrame.Position)
    scaledDirection *= (scaledDirection.Magnitude - 1) / scaledDirection.Magnitude

Nenhuma estratégia de anti-exploit é abrangente, mas é importante considerar como os jogadores maliciosos podem abordar sua experiência para que você possa colocar cheques em seu lugar para que o servidor possa executar para detectar comportamentos suspeitos.

Reduzir a saúde do jogador

Depois de verificar que um jogador marcou outro jogador, os passos finais na conclusão do loop de jogo principal na experiência de rastreamento de laser são reduzir a saúde do jogador marcado, incrementar a tabela de classificação e respawnar o jogador de volta para a rodada.

Começando com a redução da saúde do jogador atacado, Spawning and Respawning cobre a distinção entre Player e Class.

As experiências armazenam valores de dano na atributo damagePerHit de cada blaster. Por exemplo

Health não aceita valores negativos, então onPlayerTagged tem alguma lógica para manter a saúde do jogador em ou acima de zero. Depois de verificar que a saúde do jogador está acima de zero, ele compara a saúde para damagePerHit e

Essa abordagem pode parecer um pouco confusa. Por exemplo, por que não apenas definir a saúde do jogador para zero se for negativo? A razão é porque definir os valores de saúde contorna o campo de força. Usando o método Humanoid:TakeDamage(), garante que os jogadores não sofram dano enquanto seus campos de força estiverem ativos.

em Jogador Vinculado

local function onPlayerTagged(playerBlasted: Player, playerTagged: Player, damageAmount: number)
local character = playerTagged.Character
local isFriendly = playerBlasted.Team == playerTagged.Team
-- Desabilitar Iniciar / executaramigável
if isFriendly then
return
end
local humanoid = character and character:FindFirstChild("Humanoid")
if humanoid and humanoid.Health > 0 then
-- Evite saúde negativa
local damage = math.min(damageAmount, humanoid.Health)
-- TakeDamage garante que a saúde não seja reduzida se o ForceField estiver ativo
humanoid:TakeDamage(damage)
if humanoid.Health <= 0 then
-- Atribuiu um ponto para o jogadorBlasted um ponto por marcar o jogadorTagged
Scoring.incrementScore(playerBlasted, 1)
end
end
end

O próximo passo é incrementar a tabela de classificação. Pode ter parecido desnecessário para LaserBlastHandler incluir o jogador que explodiu ao lado dos dados de explosão, mas sem essa informação, a experiência não pode créditar o jogador com a etiqueta de alguém fora. Finalmente, o jogador etiquetado respawna de volta à rodada, que você pode revisar em Spawning and Respawning.

Os cinco capítulos neste tutorial cobrem o loop de jogo principal da experiência, mas ainda há muitas áreas para explorar, como:

  • Visuais do Blaster : Veja ReplicatedStorage > FirstPersonBlasterVisuals e 0> ServerScriptService 0> > 3> ThirdPersonBlasterVisuals 3> .
  • Áudio : Veja Armazenamento Replicado > Gerenciador de Som .
  • Modos Personalizados : Como você pode modificar essa experiência para introduzir novos tipos de objetivos, como pontuação do maior número de pontos antes que o tempo acabe?

Para uma lógica de jogo estendida para a experiência de laser tag, bem como recursos ambientais reutilizáveis e de alta qualidade, revise o Laser Tag modelo de tema.