Detectando golpes

*Este contenido se traduce usando la IA (Beta) y puede contener errores. Para ver esta página en inglés, haz clic en aquí.

Detectar golpes es el proceso de identificación de qué momento los explosivos chocan con los jugadores, luego reduciendo su salud en consecuencia. En un nivel alto, puede pensar en este trabajo como:

  1. Un cheque físicamente simulado de si un proyectil golpeó el objetivo.
  2. Una verificación instantánea de si el bláster estaba apuntado al objetivo.

El tipo de detección de golpes que usa depende de los requisitos del juego de tu experiencia. Por ejemplo, un cheque físicamente simulado es adecuado para una experiencia de dodgeball donde las bolas necesitan salir de la mano en una velocidad determinada, dejar como cuando se mueven a través del aire o cambiar la dirección desde las condiciones meteorológicas. Sin embargo, un cheque instantáneo es mejor para una experiencia de etiqueta láser donde los rayos deben tener una velocidad

Usando la experiencia de etiqueta láser como referencia como referencia, esta sección del tutorial te enseña sobre los scripts detrás de la detección de golpes en el espacio 3D, incluida la orientación sobre:

  • Obteniendo la dirección de la explosión de las valores de la cámara actual y el introducirde bláster del jugador.
  • Proyecta rayos en un camino recto desde el láser mientras éste explota.
  • Validar la explosión para evitar la explotación de los datos del bláster.
  • Reducción de la salud del jugador según el daño explosivo de cada tipo de blaster y la cantidad de rayos que golpean al jugador.

Después de completar esta sección, puede explorar temas de desarrollo adicionales para mejorar su juego, como sonido, audio, iluminación y efectos especiales.

Obtener dirección de explosión

Después de que un jugador lance su bláster, Almacenamiento Replicado > Bláster > intentBlastClient > 1> blastClient 1> > 4> generateBlastData4> llama a dos funciones para comenzar el proceso de detección de golpes: 7> rayDirections()7> y 0> rayResults()

generar datos de explosión

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

Las entradas para rayDirections son claras: la posición y valores de rotación actuales de la cámara, y el introducirde láser del jugador. Si la experiencia de etiqueta láser solo proporciona a los jugadores láseres que producen un solo rayo láser, ReplicatedStorage > Laser

Sin embargo, porque la muestra proporciona un tipo de bláster adicional que produce varios rayos láser con un ancho, distribución horizontal, getDirectionsForBlast debe calcular la dirección para cada rayo láser de la distribución según sus ángulos dentro de la configuración del bláster:

obtenerDireccionesParaBlast

if numLasers == 1 then
-- Para los láseres individuales, apuntan recto
table.insert(directions, originCFrame.LookVector)
elseif numLasers > 1 then
-- Para múltiples láseres, esparcelas horizontalmente
-- en un rango de distancia de láser de alrededor del 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 demostrar este concepto más aún, si usted incluiera un tercer tipo de bláster con un ancho, vertical espaldo, usted podría crear un nuevo atributo de bláster, como spreadDirection , luego ajustar la calculadora de CFrame para usar un eje diferente. Por ejemplo, observe la diferencia en las calculadoras


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

En última instancia, la función rayDirections() devuelve una tabla de Vectors que representa la dirección de cada rayo láser. Si es útil, puede agregar algo de seguimiento para obtener una idea de lo que se ve este dato.

generar datos de explosión

local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)
for _, direction in rayDirections do -- nueva línea
print(direction) -- nueva línea
end -- nueva línea
local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)

Rayos de lanzamiento

castLaserRay() , la segunda función en ReplicatedStorage > Blaster > 0> intentBlastClient

Esta información es particularmente útil para las experiencias de primer personaje porque te permite ver cuando y dónde se interponen las explosiones con los jugadores o el entorno, ambiente. Por ejemplo, la siguiente imagen muestra dos rayos que están rayando paralelos el uno al otro. Según su punto de origen y dirección, Ray A falla el muro y continúa hasta que se alcanza su máxima distancia, mientras que Ray B colisiona con el muro. Para obtener más información sobre este proceso,

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

Los parámetros castLaserRay() establecen que Raycast() llamadas deben considerar cada parte en el espacio de trabajo excepto el personaje que disparó. El script luego castiza un rayo para cada dirección en la tabla 1> dirigir1>. Si un rayo golpea algo, genera un 4>Datatype.RaycastResult

El valor de Instance es la propiedad más crítica de estas propiedades para el juego de la etiqueta láser para la experiencia porque comunica cuando los rayos se chocan con otros jugadores. Para recuperar esta información, la experi

castLaserRay() then uses Position and Normal to create a new 0> Datatype.CFrame

castLaserRay

if result then
-- El explosivo golpeó algo, compruebe si era un jugador.
destination = CFrame.lookAt(result.Position, result.Position + result.Normal)
taggedPlayer = getPlayerFromDescendant(result.Instance)
else
-- La explosión no golpeó nada, así que su destino es
-- el punto en su distancia máxima.
local distantPosition = origin + rayDirection * MAX_DISTANCE
destination = CFrame.lookAt(distantPosition, distantPosition - rayDirection)
taggedPlayer = nil
end

Validar la explosión

Para evitar cheating, el capítulo anterior implementando Blasters explica cómo blastClient notifica al servidor del blast using a RemoteEvent para que pueda

  1. Primero, getValidatedRayResults llamadas validateRayResult para comprobar que cada entrada en la rayResults tabla del cliente es un 1> Datatype.CFrame1> y un 4> Player4> (o nulo).

  2. A continuación, llama a isRayAngleFromOriginValid para comparar los ángulos esperados del laser con los del cliente. Este código en particular muestra la ventaja de usar ReplicatedStorage porque el servidor puede llamar a getDirectionsForBlast a sí mismo, almacenar el regreso como los datos "esperados" y luego compararlo con los datos del cliente.

    Al igual que la validación de blaster del capítulo anterior, isRayAngleFromOriginValid se basa en un valor de tolerancia para determinar qué constituye una "diferencia excesiva" en los ángulos:

    isRayAngleFromOriginValido

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

    Roblox abstrae los bits más implicados de la matemática, por lo que el resultado es una función de ayudante corta y altamente reutilizable con aplicabilidad en una variedad de experiencias:

    obtenerAngleBetweenDirections

    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. La siguiente prueba es la más intuitiva. Mientras que getValidatedBlastData usa DISTANCE_SANITY_CHECK_TOLERANCE_STUDS para verificar que el jugador que disparó estaba cerca del punto de origen del rayo, isPlayerNearPosition usa lógica idéntica para verificar si el jugador etiquetado estaba cerca del punto de destino

    isPlayerNearPosition

    local distanceFromCharacterToPosition = position - character:GetPivot().Position
    if distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS then
    return false
    end
  4. La última verificación isRayPathObstructed usa una variante de la operación de raycast para verificar si la dirección del rayo está detrás de una pared o otra obstrución desde la posición del cliente. Por ejemplo, si un jugador malicioso eliminara sistemáticamente todas las paredes de la experiencia para etiquetar a otros jugadores, el servidor verificaría y confirmaría que los rayos son inválidos porque conoce cada posición de objeto dentro del entorno, ambiente.

    isRayPathObscuro

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

Ninguna estrategia antiexplosión es completa, pero es importante considerar cómo los jugadores maliciosos pueden acercarse a su experiencia para que pueda poner en marcha controles que el servidor puede ejecutar para detectar comportamientos sospechosos.

Reducir la salud del jugador

Después de verificar que un jugador etiquetó a otro jugador, los pasos finales en completar la principal experiencia de la etiqueta de juego en la muestra de etiqueta de juego son reducir la salud del jugador etiquetado, aumentar el puntaje de la tabla de clasificación y reaparecer el jugador en la ronda.

Comenzando con reducir la salud del jugador etiquetado, Generando y Respawning cubre la distinción entre Player y Class.Player.Character

Las experiencias almacenan valores de daño en el atributo damagePerHit de cada blaster. Por ejemplo

Health no acepta valores negativos, por lo que onPlayerTagged tiene algo de lógica para mantener la salud del jugador por encima de cero. Después de verificar que la salud del jugador esté por encima de cero, compara la salud con damagePerHit y

Esta manera de acercar el problema puede parecer un poco confusa. Por ejemplo, ¿por qué no simplemente establecer la salud del jugador a cero si fuera negativo? La razón es porque establecer los valores de salud evita el campo de fuerza. Usando el método Humanoid:TakeDamage(), se garantiza que los jugadores no reciban daño mientras sus campos de fuerza estén activos.

enJugadorEtiquetado

local function onPlayerTagged(playerBlasted: Player, playerTagged: Player, damageAmount: number)
local character = playerTagged.Character
local isFriendly = playerBlasted.Team == playerTagged.Team
-- Desactivar fuego desencadenar
if isFriendly then
return
end
local humanoid = character and character:FindFirstChild("Humanoid")
if humanoid and humanoid.Health > 0 then
-- Evita la salud negativa
local damage = math.min(damageAmount, humanoid.Health)
-- TakeDamage garantiza que la salud no se reduzca si ForceField está activo
humanoid:TakeDamage(damage)
if humanoid.Health <= 0 then
-- Otorga un punto a playerBlasted por etiquetar a un jugador
Scoring.incrementScore(playerBlasted, 1)
end
end
end

El siguiente paso es incrementar la tabla de clasificación. Puede que no haya parecido necesario para LaserBlastHandler incluir al jugador que disparó junto con los datos de la explosión, pero sin esa información, la experiencia no puede créditar al jugador con etiquetar a alguien fuera. Finalmente, el jugador etiquetado reaparece de vuelta en la ronda, que puedes revisar en Generando y Respawning .

Los cinco capítulos en este plan de estudios cubren la experiencia de juego central, pero todavía hay muchas áreas para explorar, como:

  • Imágenes de Blaster : See ReplicatedStorage > FirstPersonBlasterVisuals y 0> ServerScriptService0> > 3> ThirdPersonBlasterVisuals 3> .
  • Audio : See AlmacenamientoReplicado > Manipulador de sonido .
  • Modos personalizados : ¿Cómo podrías modificar esta experiencia para introducir nuevos tipos de objetivos, como anotar el mayor número de puntos antes de que se acabe el tiempo?

Para obtener una lógica de juego extendida para la experiencia de etiqueta láser, así como recursos ambientales reutilizables y de alta calidad, revisa la plantilla Etiqueta láser.