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:
- Un cheque físicamente simulado de si un proyectil golpeó el objetivo.
- 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 rectotable.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 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
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 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
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íneaprint(direction) -- nueva líneaend -- nueva línealocal 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,
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
- Distance – La distancia entre el origen del rayo y el punto de intersección.
- Material – El Enum.Material en el punto de intersección.
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_DISTANCEdestination = CFrame.lookAt(distantPosition, distantPosition - rayDirection)taggedPlayer = nilend
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
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).
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:
isRayAngleFromOriginValidolocal claimedDirection = (rayResult.destination.Position - originCFrame.Position).Unitlocal directionErrorDegrees = getAngleBetweenDirections(claimedDirection, expectedDirection)return directionErrorDegrees <= ToleranceValues.BLAST_ANGLE_SANITY_CHECK_TOLERANCE_DEGREESRoblox 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:
obtenerAngleBetweenDirectionslocal 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)endLa 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
isPlayerNearPositionlocal distanceFromCharacterToPosition = position - character:GetPivot().Positionif distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS thenreturn falseendLa ú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.
isRayPathObscurolocal 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.