Neste Tutorial, você aprenderá a lançar um laser a partir do blaster em Criar ferramentas de jogador e detectar se ele atinge ou não um jogador.
Lançamento de raios para encontrar colisões
Lançamento de raio cria um raio invisível a partir de uma posição de partida para uma direção dada com um comprimento definido.Se o raio colidir com objetos ou terreno em seu caminho, ele retornará informações sobre a colisão, como a posição e o objeto com o qual colidiu.

Encontre a localização do mouse
Antes que um laser possa ser atirado, você deve primeiro saber onde o jogador está apontando.Isso pode ser encontrado por raycasting a partir da localização do mouse 2D do jogador na tela diretamente para o mundo do jogo.O raio colidirá com o que quer que o jogador esteja mirando com o mouse.
Abra o script Controlador de Ferramentas dentro da ferramenta Blaster a partir de Criar ferramentas do jogador.Se você ainda não completou esse tutorial, você pode baixar o modelo Blaster e inseri-lo no StarterPack.
No topo do script, declare uma constante chamada MAX_MOUSE_DISTANCE com um valor de 1000 .
Crie uma função chamada 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 funções apropriadastool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Use a função GetMouseLocation de UserInputService para obter a localização do mouse 2D do jogador na tela.Atribua isso a uma variável chamada localização do mouse .
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
Agora a localização do mouse 2D é conhecida, suas propriedades X e Y podem ser usadas como parâmetros para a função Camera:ViewportPointToRay(), que cria um Ray a partir da tela no mundo do jogo 3D.
Use as propriedades X e Y do mouseLocation como argumentos para a função ViewportPointToRay().Atribua isso a uma variável chamada screenToWorldRay .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Crie um raio a partir da localização do mouse de 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
É hora de usar a função Raycast para verificar se o raio atinge um Objeto.Isso requer uma posição inicial e vetor de direção: neste exemplo, você usará as propriedades de origem e direção de screenToWorldRay .
O comprimento do vetor de direção determina até onde o raio vai viajar.O raio precisa ser tão longo quanto o MAX_MOUSE_DISTANCE, então você terá que multiplicar o vetor de direção por MAX_MOUSE_DISTANCE.
Declare uma variável chamada direçãoVector e atribua-lhe o valor de screenToWorldRay.Direction multiplicado por MAX_MOUSE_DISTANCE.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Crie um raio a partir da localização do mouse 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- O vetor de direção da unidade do raio multiplicado por uma distância máximalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEChame a função Raycast da área de trabalho, passando a propriedade Origem de screenToWorldRay como o primeiro argumento e directionVector como o segundo.Atribua isso a uma variável chamada raycastResult .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Crie um raio a partir da localização do mouse 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- O vetor de direção da unidade do raio multiplicado por uma distância máximalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Lançamento de raio da origem do raio para sua direçãolocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Informações de colisão
Se a operação de lançamento de raio encontrar um objeto atingido pelo raio, ele retornará um RaycastResult, que contém informações sobre a colisão entre o raio e o Objeto.
Propriedade RaycastResult | Descrição |
---|---|
Instância | A célula BasePart ou Terrain que o raio intersectou. |
Posição | Onde a interseção ocorreu; geralmente um ponto diretamente na superfície de uma peça ou terreno. |
Material | O material no ponto de colisão. |
Normal | O vetor normal do rosto intersecado. Isso pode ser usado para determinar para qual direção o rosto está apontando. |
A propriedade Posição será a posição do objeto sobre o qual o mouse está pairando.Se o mouse não estiver pairando sobre qualquer objeto dentro de uma distância de MAX_MOUSE_DISTANCE , raycastResult será nil .
Crie uma declaração if para verificar se raycastResult existe.
Se raycastResult tiver um valor, retorne sua propriedade Posição .
Se raycastResult for nil, então encontre o fim do raycast.Calcule a posição 3D do mouse adicionando screenToWorldRay.Origin e directionVector juntos.
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Crie um raio a partir da localização do mouse 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- O vetor de direção da unidade do raio multiplicado por uma distância máxima
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Lançamento de raio da origem do raio para sua direção
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Retorne o ponto de interseção 3D
return raycastResult.Position
else
-- Nenhum objeto foi atingido, então calcule a posição no final do raio
return screenToWorldRay.Origin + directionVector
end
end
Disparar para o alvo
Agora que a posição do mouse 3D é conhecida, ela pode ser usada como uma posição alvo para disparar um laser.Um segundo raio pode ser lançado entre a arma do jogador e a posição alvo usando a função Lançar Raio .
Declare uma constante chamada MAX_LASER_DISTANCE no topo do script e atribua-a a 500 ou ao alcance escolhido para o laser blaster.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Crie uma função chamada fireWeapon sob a função getWorldMousePosition.
Chame getWorldMousePosition e atribua o resultado a uma variável chamada posição do mouse . Esta será a posição alvo para o raycast.
-- Nenhum objeto foi atingido, então calcule a posição no final do raioreturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Desta vez, o vetor de direção para a função de lançamento de raio representará a direção da posição da ferramenta do jogador para a localização alvo.
Declare uma variável chamada direção alvo e calcule o vetor de direção subtraindo a posição da ferramenta de mouseLocation.
Normalize o vetor usando sua propriedade Unidade . Isso lhe dá uma magnitude de 1, o que facilita multiplicar por uma distância mais tarde.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Calcular um vetor de direção normalizado e multiplicar por distância a laserlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitendDeclare uma variável chamada direçãoVector e atribua a ela o targetDirection multiplicado pelo MAX_LASER_DISTANCE.
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- A direção para disparar a arma, multiplicada por uma distância máximalocal directionVector = targetDirection * MAX_LASER_DISTANCEend
Um objeto RaycastParams pode ser usado para armazenar parâmetros adicionais para a função de lançamento de raio.Ele será usado em seu laser blaster para garantir que o raycast não acidentalmente colida com o jogador que está disparando a arma.Quaisquer partes incluídas na propriedade FilterDescendantsInstances de um objeto RaycastParams serão ignoradas no raycast.
Continue a função fireWeapon e declare uma variável chamada weaponRaycastParams . Atribua um novo objeto RaycastParams a ela.
Crie uma tabela que contenha o personagem local do jogador e atribua-a à propriedade .
Lançamento de raio a partir da posição do cabo da ferramenta do jogador, em uma direção para o directionVector .Lembre-se de adicionar weaponRaycastParams como argumento desta vez.Atribua isso a uma variável chamada 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 um vetor de direção normalizado e multiplicar por distância a laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- A direção para disparar a arma multiplicada por uma distância máxima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignore o personagem do jogador para impedi-lo de se machucar
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end
Finalmente, você precisará verificar se a operação de lançamento de raio retornou um valor.Se um valor for retornado, um objeto foi atingido pelo raio e um laser pode ser criado entre a arma e o local de ataque.Se nada foi retornado, a posição final precisa ser calculada para criar o laser.
Declare uma variável vazia chamada hitPosition .
Use um se declaração para verificar se weaponRaycastResult tem um valor. Se um objeto foi atingido, atribua weaponRaycastResult.Position a hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Verifique se algum objeto foi atingido entre a posição inicial e finallocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendSe não tiver valor, calcule a posição final do raycast adicionando juntos a posição da alça da ferramenta com o ].Atribua isso a hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Verifique se algum objeto foi atingido entre a posição inicial e finallocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Calcular a posição final com base na distância máxima do laserhitPosition = tool.Handle.Position + directionVectorendendNavegue até a função toolActivated e chame a função fireWeapon para que o laser dispare sempre que o recurso for ativado.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
Verifique o objeto atingido
Para encontrar se o objeto atingido pelo laser faz parte do personagem de um jogador ou apenas é uma peça de cenário, você precisará procurar por um Humanoid, pois cada personagem tem um.
Primeiro, você precisará encontrar o modelo de personagem **** .Se uma parte do personagem foi atingida, você não pode assumir que o pai do personagem atingido seria o personagem.O laser poderia ter atingido uma parte do corpo, um acessório ou uma ferramenta, todos os quais estão localizados em diferentes partes da hierarquia do personagem.
Você pode usar FindFirstAncestorOfClass para encontrar um ancestral de modelo de personagem do objeto atingido pelo laser, se existir.Se você encontrar um modelo e ele contiver um humanoide, na maioria dos casos você pode assumir que é um personagem.
Adicione o código destacado abaixo à declaração weaponRaycastResult se para verificar se um personagem foi atingido.
-- Verifique se algum objeto foi atingido entre a posição inicial e finallocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- A instância atingida será uma filha de um modelo de personagem-- Se um humanoide for encontrado no modelo, então provavelmente é o personagem de um jogadorlocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Player hit")endendelse-- Calcular a posição final com base na distância máxima do laserhitPosition = tool.Handle.Position + directionVectorend
Agora, o laser blaster deve imprimir Player hit na janela de saída sempre que a operação de lançamento de raio atingir outro jogador.
Teste com vários jogadores
Dois jogadores são necessários para testar se o raio de arma está encontrando outros jogadores, então você precisa iniciar um servidor local.
Selecione a aba Teste no Studio.
Certifique-se de que o menu suspenso de jogadores está definido como '2 Jogadores' e clique no botão Iniciar para iniciar um servidor local com 2 clientes.Três janelas aparecerão.A primeira janela será o servidor local, as outras janelas serão os clientes para Player1 e Player2.
Em um cliente, teste a disparar o outro jogador com a arma clicando neles.“Jogador atingido” deve ser exibido na saída sempre que um jogador for atingido.
Você pode saber mais sobre a aba Teste aqui.
Encontre a posição do laser
O blaster deve disparar um feixe de luz vermelho em seu alvo.A função para isso estará dentro de um ModuleScript para que possa ser reutilizada em outros scripts mais tarde.Primeiro, o script precisará encontrar a posição em que o feixe de laser deve ser renderizado.
Crie um ModuleScript chamado LaserRenderer , filiado aos StarterPlayerScripts sob StarterPlayer.
Abra o script e renomeie a tabela de módulo para o nome do script LaserRenderer .
Declare uma variável chamada DURAÇÃO_DE_TIRO com um valor de 0.15 .Esta será a quantidade de tempo (em segundos) que o laser estará visível.
Crie uma função de LaserRenderer chamada createLaser com dois parâmetros chamados toolHandle e endPosition .
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Tempo que o laser é visível por-- Crie um feixe de laser a partir de uma posição de partida para uma posição finalfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererDeclare uma variável chamada startPosition e defina a propriedade Posição de toolHandle como seu valor.Esta será a posição do laser do jogador.
Declare uma variável chamada distância a laser e subtraia endPosition de startPosition para encontrar a diferença entre os dois vetores.Use a propriedade Magnitude deste para obter o comprimento do feixe de laser.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendDeclare uma variável lasercFrame para armazenar a posição e orientação do feixe de laser.A posição precisa ser o ponto médio do começo e do fim do feixe.Use CFrame.lookAt para criar um novo CFrame localizado em startPosition e voltado para endPosition.Multiplique isso por um novo CFrame com um valor do eixo Z de metade negativa de laserDistance para obter o ponto médio.
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
Crie a peça a laser
Agora que você sabe onde criar um feixe de laser, você precisa adicionar o próprio feixe. Isso pode ser feito facilmente com uma peça Neon.
Declare uma variável laserPart e atribua a ela uma nova instância Part.
Defina as seguintes propriedades de laserPart :
- Tamanho : Vector3.new(0.2, 0.2, distância do laser)
- CFrame : laserCFrame
- Ancorado : verdadeiro
- Pode Colidir : falso
- Cor : Color3.fromRGB(225, 0, 0) (uma cor vermelha forte)
- Material : Enum.Material.Neon
Pai laserPart para Espaço de Trabalho .
Adicione a peça ao serviço Debris para que ela seja removida após a quantidade de segundos na variável 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-- Adicione o feixe de laser ao serviço de Detritos para ser removido e limpoDebris:AddItem(laserPart, SHOT_DURATION)end
Agora a função para renderizar o feixe de laser está completa, ela pode ser chamada pelo Controlador de Ferramentas .
No topo do script Controlador de Ferramentas , declare uma variável chamada LaserRenderer e exija o script do Módulo de Reprodutor de Laser localizado em PlayerScripts.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentNa parte inferior da função fireWeapon da função, chame a função LaserRenderer createLaser usando o cabo da ferramenta e hitPosition como argumentos.
-- Calcular a posição final com base na distância máxima do laserhitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endTeste a arma clicando no botão Jogar. Um raio laser deve ser visível entre a arma e o mouse quando a ferramenta for ativada.
Controlar a avaliarde tiro de arma
As armas precisam de um atraso entre cada tiro para impedir que os jogadores causem muito dano em um curto período de tempo.Isso pode ser controlado verificando se passou tempo suficiente desde que um jogador foi demitido pela última vez.
Declare uma variável no topo do Controlador de Ferramentas chamado TAXA_DE_FOGO .Este será o tempo mínimo entre cada tiro.Dê a ele um valor de sua escolha; este exemplo usa 0,3 segundos.
Declare outra variável abaixo chamada tempoOfPreviousShot com um valor de 0 .Isso armazena a última vez que o jogador atirou e será atualizado com cada tiro.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Crie uma função chamada canShootWeapon sem parâmetros.Essa função vai olhar quanto tempo passou desde o tiro anterior e retornar verdadeiro ou falso.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Verifique se já passou tempo suficiente desde que o tiro anterior foi disparadolocal function canShootWeapon()endlocal function getWorldMousePosition()Dentro da função declare uma variável chamada currentTime ; atribua a ela o resultado de chamar a função tick().Isso retorna quanto tempo se passou, em segundos, desde o 1º de janeiro de 1970 (uma data arbitrária amplamente usada para calcular o tempo).
Subtraia o timeOfPreviousShot do currentTime e retorne falso se o resultado for menor que FIRE_RATE ; caso contrário, retorne verdadeiro .
-- Verifique se já passou tempo suficiente desde que o tiro anterior foi disparadolocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendNo final da função fireWeapon , atualize timeOfPreviousShot sempre que a arma for disparada usando tick.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endDentro da função toolActivated, crie uma declaração se e chame canShootWeapon para verificar se a arma pode ser disparada.
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
Quando você testar o blaster, deve encontrar que, não importa quão rápido você clique, sempre haverá um pequeno atraso de 0,3 segundos entre cada tiro.
Dano ao jogador
Os clientes não podem danificar outros clientes diretamente; o servidor precisa ser responsável por emitir danos quando um jogador for atingido.
Os clientes podem usar um RemoteEvent para dizer ao servidor que um personagem foi atingido.Estes devem ser armazenados em ReplicatedStorage , onde são visíveis tanto para o cliente quanto para o servidor.
Crie uma Pasta no ReplicatedStorage chamada Eventos .
Insira um Evento Remoto na pasta de eventos e nomeie-o Dano de Personagem .
Em Controlador de Ferramentas , crie variáveis no início do script para o Armazenamento Replicado e a pasta 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 = 500Substitua a declaração de impressão "Player hit" em fireWeapon com uma linha de Luau para disparar o evento remoto DamageCharacter com a variável characterModel como argumento.
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel)endendelse-- Calcular a posição final com base na distância máxima do laserhitPosition = tool.Handle.Position + directionVectorend
O servidor precisa causar dano ao jogador que foi atingido quando o evento é disparado.
Insira um Script no ServerScriptService e nomeie-o Gerenciador de Laser do Servidor .
Declare uma variável chamada LASER_DAMAGE e defina-a para 10 ou um valor de sua escolha.
Crie uma função chamada damageCharacter com dois parâmetros chamados playerFired e characterToDamage .
Dentro da função, encontre o Humanoide do personagem e subtraia LASER_DAMAGE de sua saúde.
Conecte a função damageCharacter à função de evento remoto Dano de Personagem na pasta 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-- Remover saúde do personagemhumanoid.Health -= LASER_DAMAGEendend-- Conectar eventos a funções apropriadaseventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)Teste o blaster com 2 jogadores iniciando um servidor local.Quando você atira no outro jogador, sua saúde diminuirá pelo número atribuído a LASER_DAMAGE.
Renderizar os raios de laser de outro jogador
Atualmente, o feixe de laser é criado pelo cliente que atira a arma, então apenas eles poderão ver o feixe de laser.
Se o feixe de laser foi criado no servidor, então todos poderiam vê-lo.No entanto, haveria um pequeno atraso entre o cliente atirando a arma e o servidor recebendo a informação sobre o tiro.Isso significaria que o cliente atirando na arma veria um atraso entre quando eles ativam a arma e quando eles veem o raio laser; a arma se sentiria lenta como resultado.
Para resolver esse problema, cada cliente criará seus próprios raios de laser.Isso significa que o cliente atirando na arma verá o feixe de laser imediatamente.Outros clientes experimentarão um pequeno atraso entre quando outro jogador atira e um feixe aparece.Este é o cenário de melhor caso: não há como comunicar o laser de um cliente para outros clientes mais rapidamente.
Cliente do atirador
Primeiro, o cliente precisa dizer ao servidor que disparou um laser e fornecer a posição final.
Insira um Evento Remoto na pasta de eventos no ReplicatedStorage e nomeie-o LaserFired .
Localize a função fireWeapon na scriptdo Controlador de Ferramentas .No final da função, dispare o evento remoto LaserFired usando hitPosition como argumento.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
O servidor
O servidor agora deve receber o evento que o cliente disparou e dizer a todos os clientes a posição inicial e final do feixe de laser para que eles também possam renderizá-lo.
No script Gerenciador de Laser do Servidor , crie uma função chamada playerFiredLaser acima de damageCharacter com dois parâmetros chamados playerFired e endPosition.
Conecte a função ao evento remoto LaserFired .
-- Notifique todos os clientes que um laser foi disparado para que eles possam exibir o laserlocal function playerFiredLaser(playerFired, endPosition)end-- Conectar eventos a funções apropriadaseventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
O servidor precisa da posição inicial do laser.Isso pode ser enviado pelo cliente, mas é melhor evitar confiar no cliente onde possível.A posição do cabo da arma do personagem será a posição de início, para que o servidor possa encontrá-la a partir daí.
Crie uma função getPlayerToolHandle acima da função playerFiredLaser com um parâmetro chamado player .
Use o seguinte código para pesquisar o personagem do jogador pela arma e retornar o Objetode alça.
local LASER_DAMAGE = 10-- Encontre o cabo da ferramenta que o jogador está segurandolocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Notifique todos os clientes que um laser foi disparado para que eles possam exibir o laserlocal function playerFiredLaser(playerFired, endPosition)
O servidor agora pode chamar FireAllClients no evento remoto LaserFired para enviar as informações necessárias para renderizar o laser aos clientes.Isso inclui o jogador que disparou o laser (portanto, o cliente para esse jogador não renderiza o laser duas vezes), a alça do blaster (que atua como uma posição de partida para o laser) e a posição final do laser.
Na função playerFiredLaser, chame a função getPlayerToolHandle com playerFired como argumento e atribua o valor a uma variável chamada toolHandle .
Se toolHandle existir, dispare o evento LaserFired para todos os clientes usando playerFired, toolHandle e endPosition como argumentos.
-- Notifique todos os clientes que um laser foi disparado para que eles possam exibir o laserlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
Renderizar nos clientes
Agora FireAllClients foi chamado, cada cliente receberá um evento do servidor para renderizar um raio laser.Cada cliente pode reutilizar o módulo Renderizador de Laser antes para renderizar o feixe de laser usando a posição da alça e o valor da posição final enviados pelo servidor.O jogador que disparou o feixe de laser em primeiro lugar deve ignorar este evento caso contrário eles verão 2 lasers.
Crie um LocalScript em StarterPlayerScripts chamado ClientLaserManager .
Dentro do script, exija o módulo LaserRenderer .
Crie uma função chamada createPlayerLaser com os parâmetros playerWhoShot , toolHandle e endPosition.
Conecte a função ao evento remoto LaserFired na pasta de eventos.
Na função, use um if declaração para verificar se não é igual a o LocalPlayer.
Dentro da declaração if, chame a função createLaser da LaserRenderer usando toolHandle e endPosition como argumentos.
local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))local eventsFolder = ReplicatedStorage.Events-- Exibir o laser de outro jogadorlocal function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)Teste o blaster com 2 jogadores iniciando um servidor local.Posicione cada cliente em diferentes lados do seu monitor para que você possa ver ambas as janelas de uma só vez.Quando você atira em um cliente, você deve ver o laser no outro cliente.
Efeitos de som
O efeito de som de tiro atualmente só toca no cliente que está atirando o projétil.Você precisará mover o código para tocar o som para que outros jogadores também o ouçam.
No script Controlador de Ferramentas , navegue até a função toolActivated e remova a linha que toca o som de Ativação.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendNa parte inferior da função na LaserRenderer , declare uma variável chamada som de tiro e use o método de para verificar o som Ativar .
Use um if declaração para verificar se shootingSound existe; se existir, chame sua função Jogar .
laserPart.Parent = workspace-- Adicione o feixe de laser ao serviço de Detritos para ser removido e limpoDebris:AddItem(laserPart, SHOT_DURATION)-- Reproduza o som de tiro da armalocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Segure controles remotos usando validação
Se o servidor não estiver verificando dados de solicitações recebidas, um hacker pode abusar de funções e eventos remotos e usá-los para enviar valores falsos ao servidor.É importante usar validação do lado do servidor para evitar isso.
Em sua forma atual, o evento remoto Dano de Personagem é muito vulnerável a ataques.Os hackers poderiam usar esse evento para danificar qualquer jogador que quiser no jogo sem atirar neles.
A validação é o processo de verificar se os valores enviados ao servidor são realistas. Neste caso, o servidor precisará:
- Verifique se a distância entre o jogador e a posição atingida pelo laser está dentro de um determinado limite.
- Lançar raios entre a arma que disparou o laser e a posição de acerto para garantir que o tiro fosse possível e não passasse por nenhuma parede.
Cliente
O cliente precisa enviar ao servidor a posição atingida pelo raio para que ele possa verificar se a distância é realista.
Em Controlador de Ferramentas , navegue até a linha onde o evento remoto DamageCharacter é disparado na função fireWeapon.
Adicione hitPosition como um argumento.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Servidor
O cliente agora está enviando um parâmetro extra através do evento remoto DamageCharacter, então o Gerenciador de Laser de Servidor precisa ser ajustado para aceitá-lo.
No script ServerLaserManager , adicione um parâmetro hitPosition à função damageCharacter.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Remover saúde do personagemhumanoid.Health -= LASER_DAMAGEendendAbaixo da função getPlayerToolHandle, crie uma função chamada isHitValid com três parâmetros: playerFired, characterToDamage e hitPosition.
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
A primeira verificação será a distância entre a posição de acerto e o personagem acertado.
Declare uma variável chamada MAX_HIT_PROXIMITY no topo do script e atribua-lhe um valor de 10 .Esta será a distância máxima permitida entre o hit e o personagem.Uma tolerância é necessária porque o personagem pode ter se movido ligeiramente desde que o cliente disparou o evento.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10Na função isHitValid, calcule a distância entre o personagem e a posição de acerto.Se a distância for maior que MAX_HIT_PROXIMITY então retorne falso .
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Validar distância entre o golpe do personagem e a posição de golpelocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
A segunda verificação envolverá um lançamento de raio entre a arma disparada e a posição de acerto.Se o raycast retornar um objeto que não é o personagem, você pode assumir que o tiro não foi válido porque algo estava bloqueando o tiro.
Copie o código abaixo para realizar esta verificar / conferir. Retorne verdadeiro no final da função: se chegar ao terminar/parar/sair, todas as verificações passaram.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Validar distância entre o golpe do personagem e a posição de golpelocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Verifique se está atirando através de 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)-- Se uma instância foi atingida que não era o personagem, ignore o tiroif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendDeclare uma variável na função damageCharacter chamada validShot .Atribua a ele o resultado de uma chamada à função isHitValid com três argumentos: playerFired , characterToDamage e hitPosition .
Na declaração abaixo, adicione um operador e para verificar se validShot é verdadeiro .
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")local validShot = isHitValid(playerFired, characterToDamage, hitPosition)if humanoid and validShot then-- Remover saúde do personagemhumanoid.Health -= LASER_DAMAGEendend
Agora, o evento de distorção de personagem remoto é mais seguro e impedirá que a maioria dos jogadores abusem dele.Observe que alguns jogadores maliciosos frequentemente encontrarão maneiras de contornar a validação; manter eventos remotos seguros é um esforço contínuo.
Seu laser blaster está agora completo, com um sistema básico de detecção de hit usando raycasting.Tente o tutorial Detectando a entrada do usuário para descobrir como você pode adicionar uma ação de recarga ao seu laser blaster ou criar um mapa divertido de jogo e experimentar o seu laser blaster com outros jogadores!
Código final
Controlador de Ferramentas
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
-- Verifique se já passou tempo suficiente desde que o tiro anterior foi disparado
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()
-- Crie um raio a partir da localização do mouse de 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- O vetor de direção da unidade do raio multiplicado por uma distância máxima
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Lançamento de raio da origem do roy para sua direção
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Retorne o ponto de interseção 3D
return raycastResult.Position
else
-- Nenhum objeto foi atingido, então calcule a posição no final do raio
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Calcular um vetor de direção normalizado e multiplicar por distância a laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- A direção para disparar a arma, multiplicada por uma distância máxima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignore o personagem do jogador para impedi-lo de se machucar
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Verifique se algum objeto foi atingido entre a posição inicial e final
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- A instância atingida será uma filha de um modelo de personagem
-- Se um humanoide for encontrado no modelo, então provavelmente é o personagem de um jogador
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 a posição final com base na distância máxima do laser
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 Laser
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Tempo que o laser é visível por
-- Crie um feixe de laser a partir de uma posição de partida para uma posição 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
-- Adicione o feixe de laser ao serviço de Detritos para ser removido e limpo
Debris:AddItem(laserPart, SHOT_DURATION)
-- Reproduza o som de tiro da arma
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer
Gerenciador de Laser do Servidor
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Encontre o cabo da ferramenta que o jogador está segurando
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 distância entre o golpe do personagem e a posição de golpe
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Verifique se está atirando através de 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)
-- Se uma instância foi atingida que não era o personagem, ignore o tiro
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Notifique todos os clientes que um laser foi disparado para que eles possam exibir o laser
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
-- Remover saúde do personagem
humanoid.Health -= LASER_DAMAGE
end
end
-- Conectar eventos a funções apropriadas
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Gerenciador de Laser do Cliente
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- Exibir o laser de outro jogador
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)