En este tutorial, aprenderás cómo lanzar un láser desde el bláster en Crear herramientas de jugador y detectar si golpea o no a un jugador.
Raycasting para encontrar colisiones
Raycasting crea un rayo invisible desde una posición de inicio hacia una dirección determinada con una longitud definida.Si el rayo se choca con objetos o terreno en su camino, devolverá información sobre la colisión, como la posición y el objeto con el que se chocó.

Encuentra la ubicación del mouse
Antes de que se pueda disparar un láser, debes saber primero dónde apunta el jugador.Esto se puede encontrar al raycasting desde la ubicación del mouse 2D del jugador en la pantalla directamente hacia adelante desde la cámara al mundo del juego.El rayo se colisionará con lo que el jugador esté apuntando con el ratón.
Abre el script del controlador de herramientas dentro de la herramienta Blaster de Crear herramientas para jugadores.Si aún no has completado ese tutorial, puedes descargar el modelo Blaster y insertarlo en StarterPack.
En la parte superior del script, declare una constante llamada MAX_MOUSE_DISTANCE con un valor de 1000 .
Crea una función llamada getWorldMousePosition.
local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()endlocal function toolActivated()tool.Handle.Activate:Play()end-- Conectar eventos a funciones apropiadastool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Usa la función GetMouseLocation de UserInputService para obtener la ubicación del mouse de 2D del jugador en la pantalla.Asigna esto a una variable llamada mouseLocation .
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
Ahora que se conoce la ubicación del ratón de 2D, sus propiedades X y Y se pueden usar como parámetros para la función >, que crea un desde la pantalla en el mundo del juego 3D.
Usa las propiedades X y Y de mouseLocation como argumentos para la función ViewportPointToRay().Asigna esto a una variable llamada screenToWorldRay .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Crear un rayo desde la ubicación del ratón de 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
Es hora de usar la función Raycast para comprobar si el rayo golpea un objeto.Esto requiere una posición de inicio y un fuerza vectorialde dirección: en este ejemplo, usarás las propiedades de origen y dirección de screenToWorldRay .
La longitud del vector de dirección determina hasta dónde viajará el rayo.El rayo debe ser tan largo como el MAX_MOUSE_DISTANCE , así que tendrás que multiplicar el vector de dirección por MAX_MOUSE_DISTANCE .
Declare una variable llamada direcciónVector y asigne el valor de multiplicado por >.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Crear un rayo desde la ubicación del ratón de 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- El vector de dirección de la unidad del rayo multiplicado por una distancia máximalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCELlama a la función Raycast de espacio de trabajo, pasando la propiedad Origen de screenToWorldRay como primer argumento y directionVector como segundo.Asigna esto a una variable llamada raycastResult .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Crear un rayo desde la ubicación del ratón de 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- El vector de dirección de la unidad del rayo multiplicado por una distancia máximalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Raycast desde el origen del rayo hacia su direcciónlocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Información de colisión
Si la operación de lanzamiento de rayos encuentra un objeto golpeado por el rayo, devolverá un RaycastResult , que contiene información sobre la colisión entre el rayo y el objeto.
Propiedad RaycastResult | Descripción |
---|---|
Instancia | La celda BasePart o Terrain que el rayo interseccionó. |
Posición | Donde ocurrió la intersección; por lo general, un punto directamente en la superficie de una parte o terreno. |
Materiales | El material en el punto de colisión. |
Normales | El vector normal del rostro interseccionado. Esto se puede usar para determinar hacia qué dirección apunta el rostro. |
La propiedad Posición será la posición del objeto sobre el que el mouse se desplaza.Si el mouse no se desplaza sobre ningún objeto dentro de una distancia de MAX_MOUSE_DISTANCE , raycastResult será nil .
Crea una declaración if para verificar si raycastResult existe.
Si raycastResult tiene un valor, devuelve su propiedad Posición .
Si raycastResult es nil entonces encuentra el final del raycast.Calcula la posición 3D del mouse agregando screenToWorldRay.Origin y directionVector juntos.
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Crear un rayo desde la ubicación del ratón de 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- El vector de dirección de la unidad del rayo multiplicado por una distancia máxima
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast desde el origen del rayo hacia su dirección
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Devuelve el punto de intersección de 3D
return raycastResult.Position
else
-- No se golpeó ningún objeto, así que calcula la posición al final del rayo
return screenToWorldRay.Origin + directionVector
end
end
Fuego hacia el objetivo
Ahora que se conoce la posición del ratón 3D, se puede utilizar como posición de objetivo para disparar un láser hacia .Un segundo rayo se puede lanzar entre la arma del jugador y la posición objetivo usando la función Raycast .
Declare una constante llamada MAX_LASER_DISTANCE en la parte superior del script y asignársela a 500 o al rango elegido para el láser blaster.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Crea una función llamada fireWeapon bajo la función getWorldMousePosition.
Llama a getWorldMousePosition y asigna el resultado a una variable llamada posición del mouse . Esta será la posición objetivo para el lanzamiento de rayos.
-- No se golpeó ningún objeto, así que calcula la posición al final del rayoreturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Esta vez, el vector de dirección para la función de lanzamiento de rayos representará la dirección desde la posición de la herramienta del jugador hasta la ubicación objetivo.
Declare una variable llamada dirección objetivo y calcule el vector de dirección restrayendo la posición de la herramienta de mouseLocation.
Normaliza el vector usando su propiedad Unidad . Esto le da una magnitud de 1, lo que facilita multiplicarla por una longitud más tarde.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Calcular un vector de dirección normalizado y multiplicar por la distancia láserlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitendDeclare una variable llamada direcciónVector y asigne a ella el targetDirection multiplicado por el MAX_LASER_DISTANCE.
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- La dirección para disparar el arma, multiplicada por una distancia máximalocal directionVector = targetDirection * MAX_LASER_DISTANCEend
Un objeto RaycastParams se puede utilizar para almacenar parámetros adicionales para la función de lanzamiento de rayos.Se usará en su láser blaster para asegurarse de que el raycast no choque accidentalmente con el jugador que dispara el arma.Cualquier parte incluida en la propiedad de un objeto RaycastParams se ignorará en el raycast.
Continúa la función fireWeapon y declara una variable llamada weaponRaycastParams . Asigna un nuevo objeto de RaycastParams a ella.
Crea una tabla que contenga el personaje local del jugador y asignarlo a la propiedad .
Lanzamiento de rayos desde la posición del mango de la herramienta del jugador, en una dirección hacia el directionVector.Recuerde agregar weaponRaycastParams como argumento esta vez.Asigna esto a una variable llamada weaponRaycastResult .
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local tool = script.Parent
local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 500
local function getWorldMousePosition()
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Calcular un vector de dirección normalizado y multiplicar por la distancia láser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La dirección para disparar el arma multiplicada por una distancia máxima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignora el personaje del jugador para evitar que se dañen a sí mismos
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end
Por último, tendrás que comprobar que la operación de lanzamiento de rayos devolvió un valor.Si se devuelve un valor, un objeto fue golpeado por el rayo y se puede crear un láser entre el arma y la ubicación de golpe.Si no se devolvió nada, la posición final debe calcularse para crear el láser.
Declare una variable vacía llamada hitPosition .
Usa una declaración si para verificar si weaponRaycastResult tiene un valor. Si se golpeó un objeto, asigna weaponRaycastResult.Position a hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Compruebe si algún objeto fue golpeado entre la posición de inicio y finallocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendSi weaponRaycastResult no tiene valor, calcular la posición final del raycast agregando juntos la posición **** del mango de la herramienta con el directionVector.Asigna esto a hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Compruebe si algún objeto fue golpeado entre la posición de inicio y finallocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Calcular la posición final basada en la distancia máxima del láserhitPosition = tool.Handle.Position + directionVectorendendNavegue hasta la función toolActivated y llame a la función fireWeapon para que el láser dispare cada vez que se active la herramienta.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
Compruebe el objeto golpeado
Para encontrar si el objeto golpeado por el láser es parte del personaje de un jugador o solo una pieza de paisaje, necesitarás buscar un Humanoid , ya que cada personaje tiene uno.
Primero, necesitarás encontrar el modelo de caracteres **** .Si se golpeó una parte del personaje, no puedes asumir que el padre del personaje golpeado sería el personaje.El láser podría haber golpeado una parte del cuerpo, un accesorio o una herramienta, todas las cuales se encuentran en diferentes partes de la jerarquía del personaje.
Puedes usar FindFirstAncestorOfClass para encontrar un ancestro de modelo de personaje del objeto golpeado por el láser, si existe uno.Si encuentras un modelo y contiene un humanoide, en la mayoría de los casos puedes asumir que es un personaje.
Añade el código resaltado a continuación a la declaración weaponRaycastResult si para comprobar si un personaje fue golpeado.
-- Compruebe si algún objeto fue golpeado entre la posición de inicio y finallocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- La instancia golpeada será un hijo de un modelo de aplicación de modelado-- Si se encuentra un humanoide en el modelo, es probable que sea el personaje de un jugadorlocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Player hit")endendelse-- Calcular la posición final basada en la distancia máxima del láserhitPosition = tool.Handle.Position + directionVectorend
Ahora el láser blaster debería imprimir Player hit en la ventana de salida cada vez que la operación de lanzamiento de rayos golpee a otro jugador.
Prueba con múltiples jugadores
Se necesitan dos jugadores para probar si el rayo de armas está encontrando a otros jugadores, por lo que debe iniciar un servidor local.
Seleccione la pestaña Prueba en Studio.
Asegúrese de que el menú desplegable de jugadores esté configurado como '2 jugadores' y haga clic en el botón de inicio para iniciar un servidor local con 2 clientes.Tres ventanas aparecerán.La primera ventana será el servidor local, las otras ventanas serán los clientes para Player1 y Player2.
En un cliente, prueba disparar al otro jugador con la arma haciendo clic en él.El "Golpe de jugador" debe mostrarse en la salida cada vez que se dispara a un jugador.
Puede obtener más información sobre la pestaña Prueba aquí.
Encuentra la posición del láser
El bláster debe disparar un rayo rojo de luz a su objetivo.La función para esto estará dentro de un ModuleScript por lo que se podrá reutilizar en otros scripts más tarde.Primero, el script deberá encontrar la posición en la que se debe renderizar el rayo láser.
Crea un ModuleScript llamado LaserRenderer que pertenece a StarterPlayerScripts bajo StarterPlayer.
Abre el script y renombra la tabla de módulos al nombre del script LaserRenderer .
Declare una variable llamada DURACIÓN DE DISPARO con un valor de 0.15 .Esta será la cantidad de tiempo (en segundos) que el láser estará visible.
Crea una función de LaserRenderer llamada createLaser con dos parámetros llamados toolHandle y endPosition .
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Tiempo que el láser está visible para-- Crear un rayo láser desde una posición de inicio hacia una posición finalfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererDeclare una variable llamada startPosition y establezca la propiedad Posición de toolHandle como su valor.Esta será la posición del láser blaster del jugador.
Declara una variable llamada distancia láser y resta endPosition de startPosition para encontrar la diferencia entre los dos vectores.Usa la propiedad Magnitud de esto para obtener la longitud del rayo láser.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendDeclare una variable laserCFrame para almacenar la posición y orientación del rayo láser.La posición debe ser el punto medio del comienzo y el final del rayo.Usa CFrame.lookAt para crear un nuevo CFrame ubicado en startPosition y dirigido hacia endPosition .Multiplica esto por un nuevo CFrame con un valor del eje Z de la mitad de negativo laserDistance para obtener el punto medio.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)end
Crear la parte láser
Ahora que sabe dónde crear un rayo láser, debe agregar el propio rayo. Esto se puede hacer fácilmente con una pieza de neón.
Declare una variable laserPart y asigne a ella una nueva instancia Part.
Establezca las siguientes propiedades de laserPart :
- Tamaño : Vector3.new(0.2, 0.2, distancia láser)
- CFrame : laserCFrame
- Anclado : verdadero
- Puede colisionar : false
- Color : Color3.fromRGB(225, 0, 0) (un color rojo fuerte)
- Material : Enum.Material.Neón
Padre laserPart a Espacio de trabajo .
Añade la parte al servicio Debris para que se elimine después de la cantidad de segundos en la variable SHOT_DURATION.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)local laserPart = Instance.new("Part")laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)laserPart.CFrame = laserCFramelaserPart.Anchored = truelaserPart.CanCollide = falselaserPart.Color = Color3.fromRGB(225, 0, 0)laserPart.Material = Enum.Material.NeonlaserPart.Parent = workspace-- Añade un rayo láser al servicio de escombros para ser eliminado y limpiadoDebris:AddItem(laserPart, SHOT_DURATION)end
Ahora que la función para renderizar el rayo láser está completa, se puede llamar por el Controlador de herramientas .
En la parte superior del script Controlador de herramientas , declare una variable llamada LaserRenderer y requiera el script del módulo LaserRenderer ubicado en PlayerScripts.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentAl final de la función fireWeapon de la función, llama a la función LaserRenderer createLaser usando el control del instrumento y hitPosition como argumentos.
-- Calcular la posición final basada en la distancia máxima del láserhitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endPrueba la arma haciendo clic en el botón de reproducir. Un rayo láser debe ser visible entre la arma y el ratón cuando la herramienta esté activada.
Controlar la puntuarde disparo de las armas
Las armas necesitan un retraso entre cada disparo para evitar que los jugadores causen demasiado daño en un corto período de tiempo.Esto se puede controlar verificando si se ha pasado suficiente tiempo desde que un jugador disparó por última vez.
Declare una variable en la parte superior del Controlador de herramientas llamado Tasa de fuego .Este será el tiempo mínimo entre cada disparo.Dale un valor de tu elección; este ejemplo usa 0.3 segundos.
Declare otra variable debajo llamada tiempo de disparo anterior con un valor de 0 .Esto almacena la última vez que el jugador disparó y se actualizará con cada disparo.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Crea una función llamada canShootWeapon sin parámetros.Esta función verá cuánto tiempo ha pasado desde el último disparo y devolverá verdadero o falso.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Compruebe si se ha pasado suficiente tiempo desde que se disparó el último disparolocal function canShootWeapon()endlocal function getWorldMousePosition()Dentro de la función declare una variable llamada tiempo actual ; asignarle el resultado de llamar la función tick().Esto devuelve cuánto tiempo ha pasado, en segundos, desde el 1 de enero de 1970 (una fecha arbitraria ampliamente utilizada para calcular el tiempo).
Resta el timeOfPreviousShot de currentTime y devuelve falso si el resultado es más pequeño que FIRE_RATE ; de lo contrario, devuelve verdadero .
-- Compruebe si se ha pasado suficiente tiempo desde que se disparó el último disparolocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendAl final de la función fireWeapon actualizar timeOfPreviousShot cada vez que se dispara el arma usando tick .
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endDentro de la función toolActivated, crea una declaración si y llama a canShootWeapon para verificar si la arma se puede disparar.
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
Cuando pruebes el bláster deberás encontrar que, sin importar la rapidez con la que hagas hcer clic, siempre habrá un breve retraso de 0,3 segundos entre cada disparo.
Daña al jugador
Los clientes no pueden dañar a otros clientes directamente; el servidor debe ser responsable de emitir daños cuando un jugador sea golpeado.
Los clientes pueden usar un RemoteEvent para decirle al servidor que un personaje ha sido golpeado.Estos deben almacenarse en ReplicatedStorage , donde son visibles tanto para el cliente como para el servidor.
Crea una carpeta en ReplicatedStorage llamada Eventos .
Inserta un evento remoto en la carpeta de eventos y nombralo Daño de personaje .
En Controlador de herramientas , crea variables al comienzo del script para ReplicatedStorage y la carpeta de eventos.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.Parentlocal eventsFolder = ReplicatedStorage.Eventslocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Reemplazar la declaración de impresión con una línea de Luau para disparar el evento remoto Daño de personaje con la variable como argumento.
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel)endendelse-- Calcular la posición final basada en la distancia máxima del láserhitPosition = tool.Handle.Position + directionVectorend
El servidor debe infligir daño al jugador que ha sido golpeado cuando se dispara el evento.
Inserte un script en ServerScriptService y llámalo ServerLaserManager .
Declare una variable llamada LASER_DAMAGE y establezca el valor en 10 o un valor de su elección.
Crea una función llamada damageCharacter con dos parámetros llamados playerFired y characterToDamage .
Dentro de la función, encuentra el Humanoide del personaje y resta LASER_DAMAGE de su salud.
Conecta la función damageCharacter a el evento remoto Daño de personaje en la carpeta de eventos.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10function damageCharacter(playerFired, characterToDamage)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Quitar la salud del personajehumanoid.Health -= LASER_DAMAGEendend-- Conectar eventos a funciones apropiadaseventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)Prueba el blaster con 2 jugadores iniciando un servidor local.Cuando disipes al otro jugador, su salud disminuirá por el número asignado a LASER_DAMAGE.
Renderizar los rayos láser de otro jugador
Actualmente, el rayo láser se crea por el cliente que dispara el arma, por lo que solo ellos podrán ver el rayo láser.
Si el rayo láser se creó en el servidor, entonces todos podrían verlo.Sin embargo, habría un pequeño retraso entre el cliente que dispara el arma y el servidor que recibe la información sobre el disparo.Esto significaría que el cliente disparando el arma vería un retraso entre cuando activan el arma y cuando ven el rayo láser; el arma se sentiría lenta como resultado.
Para resolver este problema, cada cliente creará sus propios rayos láser.Esto significa que el cliente que dispara el arma verá instantáneamente el rayo láser.Otros clientes experimentarán un pequeño retraso entre cuando otro jugador dispara y aparece un rayo.Este es el mejor escenario de caso: no hay forma de comunicar el láser de un cliente a otros clientes más rápido.
Cliente del tirador
Primero, el cliente debe decirle al servidor que ha disparado un láser y proporcionar la posición final.
Inserta un evento remoto en la carpeta de eventos en ReplicatedStorage y ponle el nombre disparado por láser .
Localiza la función fireWeapon en el script Controlador de herramientas .Al final de la función, activa el evento remoto LaserFired usando hitPosition como argumento.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
El servidor
El servidor ahora debe recibir el evento que el cliente ha disparado y decirle a todos los clientes la posición de inicio y final del rayo láser para que también lo puedan renderizar.
En el script ServerLaserManager , crea una función llamada playerFiredLaser por encima de damageCharacter con dos parámetros llamados playerFired y endPosition.
Conecta la función al evento remoto LaserFired .
-- Notifica a todos los clientes que se ha disparado un láser para que puedan mostrar el láserlocal function playerFiredLaser(playerFired, endPosition)end-- Conectar eventos a funciones apropiadaseventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
El servidor necesita la posición de inicio del láser.Esto podría enviarse desde el cliente, pero es mejor evitar confiar en el cliente donde sea posible.La posición del mango de la arma del personaje será la posición de inicio, por lo que el servidor puede encontrarla desde allí.
Crea una función getPlayerToolHandle por encima de la función playerFiredLaser con un parámetro llamado player .
Usa el siguiente código para buscar el personaje del jugador por la arma y devolver el objeto de agarre.
local LASER_DAMAGE = 10-- Encuentra el mango de la herramienta que el jugador está sosteniendolocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Notifica a todos los clientes que se ha disparado un láser para que puedan mostrar el láserlocal function playerFiredLaser(playerFired, endPosition)
El servidor ahora puede llamar FireAllClients al evento remoto LaserFired para enviar la información requerida para renderizar el láser a los clientes.Esto incluye al jugador que disparó el láser (por lo que el cliente para ese jugador no renderiza el láser dos veces), el mango del bláster (que actúa como una posición de inicio para el láser) y la posición final del láser
En la función playerFiredLaser, llama a la función getPlayerToolHandle con playerFired como argumento y asigna el valor a una variable llamada toolHandle .
Si toolHandle existe, dispara el evento LaserFired para todos los clientes que usan playerFired , toolHandle y endPosition como argumentos.
-- Notifica a todos los clientes que se ha disparado un láser para que puedan mostrar el láserlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
Renderizar en los clientes
Ahora FireAllClients se ha llamado, cada cliente recibirá un evento del servidor para renderizar un rayo láser.Cada cliente puede reutilizar el módulo Renderizador láser anterior para renderizar el rayo láser usando la posición de manejo y la posición final del herramientaenviada por el servidor.El jugador que disparó el rayo láser en primer lugar debe ignorar este evento de lo contrario verán 2 láseres.
Crea un LocalScript en StarterPlayerScripts llamado ClientLaserManager .
Dentro del script, requiere el módulo LaserRenderer .
Crea una función llamada createPlayerLaser con los parámetros playerWhoShot , toolHandle y endPosition .
Conecta la función al evento remoto LaserFired en la carpeta de eventos.
En la función, utilice una declaración si para verificar si no es igual a LocalPlayer.
Dentro de la declaración if, llama a la función createLaser de LaserRenderer del módulo usando toolHandle y endPosition como argumentos.
local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))local eventsFolder = ReplicatedStorage.Events-- Mostrar el láser de otro jugadorlocal function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)Prueba el blaster con 2 jugadores iniciando un servidor local.Coloca a cada cliente en diferentes lados de tu monitor para que puedas ver ambas ventanas a la vez.Cuando dispares en un solo cliente deberías ver el láser en el otro cliente.
Efectos de sonido
El efecto de sonido de disparo actualmente solo se reproduce en el cliente que dispara el proyectil.Necesitarás mover el código para reproducir el sonido para que otros jugadores también lo escuchen.
En el script Controlador de herramientas , navegue hasta la función herramientaActivada y elimine la línea que reproduce el sonido de Activar.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendEn la parte inferior de la función createLaser de LaserRenderer , declare una variable llamada disparo de sonido y use el método FindFirstChild() de toolHandle para verificar el sonido Activar .
Usa una declaración si para verificar si existe shootingSound; si lo hace, llama a su función Jugar .
laserPart.Parent = workspace-- Añade un rayo láser al servicio de escombros para ser eliminado y limpiadoDebris:AddItem(laserPart, SHOT_DURATION)-- Reproduce el sonido de disparo de la armalocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Asegurar controles remotos usando validación
Si el servidor no está verificando los datos de las solicitudes entrantes, un pirata informático puede abusar de las funciones y eventos remotos y usarlos para enviar valores falsos al servidor.Es importante utilizar validación del lado del servidor para evitar esto.
En su forma actual, el evento remoto DamageCharacter es muy vulnerable al ataque.Los piratas informáticos podrían usar este evento para dañar a cualquier jugador que quieran en el juego sin dispararles.
La validación es el proceso de verificar que los valores que se envían al servidor sean realistas. En este caso, el servidor necesitará:
- Compruebe si la distancia entre el jugador y la posición golpeada por el láser está dentro de un cierto límite.
- Lanzar rayos entre el arma que disparó el láser y la posición de golpe para asegurarse de que el disparo fuera posible y no pasara por ninguna pared.
Cliente
El cliente debe enviar al servidor la posición golpeada por el raycast para que pueda verificar que la distancia es realista.
En Controlador de herramientas , navegue hasta la línea en la que se ejecuta el evento remoto de Daño de personaje en la función fireWeapon.
Añade hitPosition como argumento.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Servidor
El cliente ahora está enviando un parámetro adicional a través del evento remoto DamageCharacter, por lo que el ServerLaserManager debe ajustarse para aceptarlo.
En el script ServerLaserManager , añade un parámetro hitPosition a la función damageCharacter.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Quitar la salud del personajehumanoid.Health -= LASER_DAMAGEendendDebajo de la función getPlayerToolHandle , cree una función llamada isHitValid con tres parámetros: playerFired , characterToDamage y hitPosition .
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
La primera comprobación será la distancia entre la posición de golpe y el personaje golpeado.
Declare una variable llamada MAX_HIT_PROXIMITY en la parte superior del script y asigne un valor de 10 .Esta será la distancia máxima permitida entre el golpe y el personaje.Se necesita una tolerancia porque el personaje puede haberse movido ligeramente desde que el cliente disparó el evento.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10En la función isHitValid, calcula la distancia entre el personaje y la posición de golpe.Si la distancia es mayor que MAX_HIT_PROXIMITY entonces devuelve falso .
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Validar la distancia entre el golpe de personaje y la posición de golpelocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
La segunda comprobación implicará un raycast entre el arma disparada y la posición de golpe.Si el raycast devuelve un objeto que no es el personaje, puedes asumir que el disparo no fue válido porque algo estaba bloqueando el disparo.
Copia el código a continuación para realizar esta verificar, comprobar. Devuelve verdadero al final de la función: si llega al finalizar, todas las comprobaciones han pasado.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Validar la distancia entre el golpe de personaje y la posición de golpelocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Compruebe si dispara a través de las paredeslocal toolHandle = getPlayerToolHandle(playerFired)if toolHandle thenlocal rayLength = (hitPosition - toolHandle.Position).Magnitudelocal rayDirection = (hitPosition - toolHandle.Position).Unitlocal raycastParams = RaycastParams.new()raycastParams.FilterDescendantsInstances = {playerFired.Character}local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)-- Si se golpeó una instancia que no era el personaje, ignora el disparoif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendDeclare una variable en la función damageCharacter llamada disparo válido .Asigne al resultado de una llamada a la función isHitValid con tres argumentos: playerFired , characterToDamage y hitPosition .
En la declaración if a continuación, agregue un operador y para verificar si validShot es verdadero .
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")local validShot = isHitValid(playerFired, characterToDamage, hitPosition)if humanoid and validShot then-- Quitar la salud del personajehumanoid.Health -= LASER_DAMAGEendend
Ahora el evento remoto de dañoCharacter es más seguro y evitará que la mayoría de los jugadores lo abusen.Tenga en cuenta que algunos jugadores maliciosos a menudo encontrarán formas de evitar la validación; mantener los eventos remotos seguros es un esfuerzo continuo.
Su láser blaster está ahora completo, con un sistema de detección de golpes básico que utiliza intersección rayo-superficie, emisión de rayos.Prueba el tutorial Detección de la entrada del usuario para descubrir cómo puedes agregar una acción de recarga a tu láser blaster, o crear un mapa divertido de juego y probar tu láser blaster con otros jugadores!
Código final
Controlador de herramientas
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
local tool = script.Parent
local eventsFolder = ReplicatedStorage.Events
local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 500
local FIRE_RATE = 0.3
local timeOfPreviousShot = 0
-- Compruebe si se ha pasado suficiente tiempo desde que se disparó el último disparo
local function canShootWeapon()
local currentTime = tick()
if currentTime - timeOfPreviousShot < FIRE_RATE then
return false
end
return true
end
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Crear un rayo desde la ubicación del ratón de 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- El vector de dirección de la unidad del rayo multiplicado por una distancia máxima
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast desde el origen del roy hacia su dirección
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Devuelve el punto de intersección de 3D
return raycastResult.Position
else
-- No se golpeó ningún objeto, así que calcula la posición al final del rayo
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Calcular un vector de dirección normalizado y multiplicar por la distancia láser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La dirección para disparar el arma, multiplicada por una distancia máxima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignora el personaje del jugador para evitar que se dañen a sí mismos
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Compruebe si algún objeto fue golpeado entre la posición de inicio y final
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- La instancia golpeada será un hijo de un modelo de aplicación de modelado
-- Si se encuentra un humanoide en el modelo, es probable que sea el personaje de un jugador
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
if characterModel then
local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
if humanoid then
eventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)
end
end
else
-- Calcular la posición final basada en la distancia máxima del láser
hitPosition = tool.Handle.Position + directionVector
end
timeOfPreviousShot = tick()
eventsFolder.LaserFired:FireServer(hitPosition)
LaserRenderer.createLaser(tool.Handle, hitPosition)
end
local function toolEquipped()
tool.Handle.Equip:Play()
end
local function toolActivated()
if canShootWeapon() then
fireWeapon()
end
end
tool.Equipped:Connect(toolEquipped)
tool.Activated:Connect(toolActivated)
Renderizador láser
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Tiempo que el láser está visible para
-- Crear un rayo láser desde una posición de inicio hacia una posición final
function LaserRenderer.createLaser(toolHandle, endPosition)
local startPosition = toolHandle.Position
local laserDistance = (startPosition - endPosition).Magnitude
local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)
local laserPart = Instance.new("Part")
laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)
laserPart.CFrame = laserCFrame
laserPart.Anchored = true
laserPart.CanCollide = false
laserPart.Color = Color3.fromRGB(255, 0, 0)
laserPart.Material = Enum.Material.Neon
laserPart.Parent = workspace
-- Añade un rayo láser al servicio de escombros para ser eliminado y limpiado
Debris:AddItem(laserPart, SHOT_DURATION)
-- Reproduce el sonido de disparo de la arma
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer
ServerLaserManager
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Encuentra el mango de la herramienta que el jugador está sosteniendo
local function getPlayerToolHandle(player)
local weapon = player.Character:FindFirstChildOfClass("Tool")
if weapon then
return weapon:FindFirstChild("Handle")
end
end
local function isHitValid(playerFired, characterToDamage, hitPosition)
-- Validar la distancia entre el golpe de personaje y la posición de golpe
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Compruebe si dispara a través de las paredes
local toolHandle = getPlayerToolHandle(playerFired)
if toolHandle then
local rayLength = (hitPosition - toolHandle.Position).Magnitude
local rayDirection = (hitPosition - toolHandle.Position).Unit
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {playerFired.Character}
local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)
-- Si se golpeó una instancia que no era el personaje, ignora el disparo
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Notifica a todos los clientes que se ha disparado un láser para que puedan mostrar el láser
local function playerFiredLaser(playerFired, endPosition)
local toolHandle = getPlayerToolHandle(playerFired)
if toolHandle then
eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
end
end
function damageCharacter(playerFired, characterToDamage, hitPosition)
local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
if humanoid and validShot then
-- Quitar la salud del personaje
humanoid.Health -= LASER_DAMAGE
end
end
-- Conectar eventos a funciones apropiadas
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Gestor de láseres cliente
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- Mostrar el láser de otro jugador
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)