Hitdetectie met lasers

*Deze inhoud is vertaald met behulp van AI (bèta) en kan fouten bevatten. Klik hier om deze pagina in het Engels te bekijken.

In deze tutorial leer je hoe je een laser kunt afvuren vanuit de blaster in Maak spelerstools en detecteren of deze een speler raakt of niet.

Raycasting om botsingen te vinden

Raycasting creëert een onzichtbare straal vanuit een startpositie in een bepaalde richting met een gedefinieerde lengte. Als de straal botsingen met objecten of terrein in zijn pad tegenkomt, retourneert het informatie over de botsing, zoals de positie en het object waarmee het in botsing kwam.

Raycast van A naar B die met een muur botst

Vind de muislocatie

Voordat een laser kan worden afgevuurd, moet je eerst weten waar de speler richt. Dit kan worden gevonden door te raycasten vanuit de 2D muislocatie van de speler op het scherm recht vooruit vanaf de camera in de 3D-wereld. De straal zal botsen met wat de speler met de muis richt.

  1. Open het ToolController script binnen de Blaster-tool van Maak spelerstools. Als je die tutorial nog niet hebt voltooid, kun je het Blaster model downloaden en in StarterPack invoegen.

  2. Declareer bovenaan het script een constante genaamd MAX_MOUSE_DISTANCE met een waarde van 1000.

  3. Maak een functie genaamd getWorldMousePosition.

    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end
    local function toolActivated()
    tool.Handle.Activate:Play()
    end
    -- Verbind evenementen met de juiste functies
    tool.Equipped:Connect(toolEquipped)
    tool.Activated:Connect(toolActivated)
  4. Gebruik de GetMouseLocation functie van UserInputService om de 2D muislocatie van de speler op het scherm te krijgen. Ken dit toe aan een variabele genaamd mouseLocation.

    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    end

Nu de 2D muislocatie bekend is, kunnen de X en Y eigenschappen worden gebruikt als parameters voor de Camera:ViewportPointToRay() functie, die een Ray creëert van het scherm naar de 3D-wereld.

  1. Gebruik de X en Y eigenschappen van mouseLocation als argumenten voor de Camera:ViewportPointToRay() functie. Ken dit toe aan een variabele genaamd screenToWorldRay.

    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Maak een straal van de 2D muislocatie
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    end

Het is tijd om de WorldRoot:Raycast() functie te gebruiken om te controleren of de straal een object raakt. Dit vereist een startpositie en richtingvector: in dit voorbeeld gebruik je de oorsprong en richtingseigenschappen van screenToWorldRay.

De lengte van de richtingvector bepaalt hoe ver de straal zal reizen. De straal moet zo lang zijn als de MAX_MOUSE_DISTANCE, dus je moet de richtingvector vermenigvuldigen met MAX_MOUSE_DISTANCE.

  1. Declareer een variabele genaamd directionVector en ken deze de waarde van screenToWorldRay.Direction vermenigvuldigd met MAX_MOUSE_DISTANCE toe.

    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Maak een straal van de 2D muislocatie
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- De eenheidsrichtingvector van de straal vermenigvuldigd met een maximale afstand
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
  2. Roep de WorldRoot:Raycast() functie van workspace aan, waarbij je de Origin eigenschap van screenToWorldRay als eerste argument en directionVector als tweede doorgeeft. Ken dit toe aan een variabele genaamd raycastResult.

    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Maak een straal van de 2D muislocatie
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- De eenheidsrichtingvector van de straal vermenigvuldigd met een maximale afstand
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
    -- Raycast van de oorsprong van de straal naar zijn richting
    local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)

Botsingsinformatie

Als de raycast-operatie een object vindt dat door de straal is geraakt, retourneert het een RaycastResult, dat informatie bevat over de botsing tussen de straal en het object.

RaycastResult EigenschapBeschrijving
InstanceDe BasePart of Terrain cel waarmee de straal is gekruist.
PositionWaar de intersectie plaatsvond; meestal een punt direct op het oppervlak van een deel of terrein.
MaterialHet materiaal op het botsingspunt.
NormalDe normale vector van het gekruiste vlak. Dit kan worden gebruikt om te bepalen welke kant het vlak op wijst.

De Position eigenschap zal de positie zijn van het object waar de muis overheen zweeft. Als de muis niet over een object zweeft binnen een afstand van MAX_MOUSE_DISTANCE, zal raycastResult nil zijn.

  1. Maak een if-statement om te controleren of raycastResult bestaat.

  2. Als raycastResult een waarde heeft, retourneer dan de Position eigenschap.

  3. Als raycastResult nil is, vind dan het einde van de raycast. Bereken de 3D-positie van de muis door screenToWorldRay.Origin en directionVector bij elkaar op te tellen.

local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Maak een straal van de 2D muislocatie
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- De eenheidsrichtingvector van de straal vermenigvuldigd met een maximale afstand
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast van de oorsprong van de straal naar zijn richting
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Retourneer het 3D-punt van intersectie
return raycastResult.Position
else
-- Geen object geraakt, dus bereken de positie aan het einde van de straal
return screenToWorldRay.Origin + directionVector
end
end

Vuur naar het doelwit

Nu de 3D muispositie bekend is, kan deze worden gebruikt als een doelpositie om een laser naartoe te vuren. Een tweede straal kan worden afgevuurd tussen de wapen van de speler en de doelpositie met behulp van de Raycast functie.

  1. Declareer een constante genaamd MAX_LASER_DISTANCE bovenaan het script en ken deze de waarde 500 toe, of je gekozen bereik voor de laser blaster.

    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  2. Maak een functie genaamd fireWeapon onder de getWorldMousePosition functie.

  3. Roep getWorldMousePosition aan en ken het resultaat toe aan een variabele genaamd mousePosition. Dit zal de doelpositie zijn voor de raycast.

    -- Geen object geraakt, dus bereken de positie aan het einde van de straal
    return screenToWorldRay.Origin + directionVector
    end
    end
    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end

Deze keer zal de richtingvector voor de raycastfunctie de richting van de positie van het gereedschap van de speler naar de doelpositie vertegenwoordigen.

  1. Declareer een variabele genaamd targetDirection en bereken de richtingvector door de positie van het gereedschap van mouseLocation af te trekken.

  2. Normaliseer de vector door de Unit eigenschap te gebruiken. Dit geeft het een grootte van 1, wat het later gemakkelijk maakt om met een lengte te vermenigvuldigen.

    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    -- Bereken een genormaliseerde richtingvector en vermenigvuldig met laserafstand
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. Declareer een variabele genaamd directionVector en ken deze de waarde van de targetDirection vermenigvuldigd met de MAX_LASER_DISTANCE toe.

    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    -- De richting om het wapen af te vuren, vermenigvuldigd met een maximale afstand
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

Een RaycastParams object kan worden gebruikt om extra parameters voor de raycastfunctie op te slaan. Dit zal in je laser blaster worden gebruikt om ervoor te zorgen dat de raycast niet per ongeluk met de speler die het wapen afvuurt botst. Alle onderdelen die zijn opgenomen in de FilterDescendantsInstances eigenschap van een RaycastParams object zullen genegeerd worden in de raycast.

  1. Ga verder met de fireWeapon functie en declareer een variabele genaamd weaponRaycastParams. Ken een nieuw RaycastParams object aan het toe.

  2. Maak een tabel met de lokale character van de speler en ken deze toe aan de weaponRaycastParams.FilterDescendantsInstances eigenschap.

  3. Raycast vanaf de positie van de handgreep van het gereedschap van de speler, in een richting naar de directionVector. Vergeet niet om weaponRaycastParams deze keer als argument toe te voegen. Ken dit toe aan een variabele genaamd 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()
-- Bereken een genormaliseerde richtingvector en vermenigvuldig met laserafstand
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- De richting om het wapen af te vuren, vermenigvuldigd met een maximale afstand
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Negeer de karakter van de speler om te voorkomen dat ze zichzelf beschadigen
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end

Ten slotte moet je controleren of de raycast-operatie een waarde heeft geretourneerd. Als er een waarde wordt geretourneerd, is er een object geraakt door de straal en kan er een laser worden gemaakt tussen het wapen en de locatie van de botsing. Als er niets wordt geretourneerd, moet de uiteindelijke positie worden berekend om de laser te maken.

  1. Declareer een lege variabele genaamd hitPosition.

  2. Gebruik een if statement om te controleren of weaponRaycastResult een waarde heeft. Als er een object is geraakt, ken dan weaponRaycastResult.Position toe aan hitPosition.

    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Controleer of er objecten zijn geraakt tussen de start- en eindpositie
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. Als weaponRaycastResult geen waarde heeft, bereken dan de eindpositie van de raycast door de positie van de handgreep van het gereedschap op te tellen bij de directionVector. Ken dit toe aan hitPosition.

    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Controleer of er objecten zijn geraakt tussen de start- en eindpositie
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    else
    -- Bereken de eindpositie op basis van de maximale laserafstand
    hitPosition = tool.Handle.Position + directionVector
    end
    end
  4. Navigeer naar de toolActivated functie en roep de fireWeapon functie aan zodat de laser elke keer wordt afgevuurd wanneer het gereedschap wordt geactiveerd.

    local function toolActivated()
    tool.Handle.Activate:Play()
    fireWeapon()
    end

Controleer het object dat is geraakt

Om te bepalen of het object dat door de laser is geraakt deel uitmaakt van het karakter van een speler of gewoon een stuk scenery is, moet je zoeken naar een Humanoid, aangezien elk karakter er een heeft.

Eerst moet je het karaktermodel vinden. Als een deel van het karakter is geraakt, kun je niet aannemen dat de ouder van het object dat is geraakt het karakter is. De laser kan een lichaamsdeel, een accessoire of een gereedschap hebben geraakt, die zich allemaal op verschillende plaatsen in de hiërarchie van het karakter bevinden.

Je kunt FindFirstAncestorOfClass gebruiken om een karaktermodel voorouder van het object dat door de laser is geraakt te vinden, als deze bestaat. Als je een model vindt en het bevat een humanoïde, kun je in de meeste gevallen aannemen dat het een karakter is.

  1. Voeg de gemarkeerde code hieronder toe aan de if statement van weaponRaycastResult om te controleren of er een karakter is geraakt.

    -- Controleer of er objecten zijn geraakt tussen de start- en eindpositie
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    -- De instantie die is geraakt, zal een kind zijn van een karaktermodel
    -- Als er een humanoïde in het model wordt gevonden, is het waarschijnlijk een karakter van een speler
    local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    print("Speler geraakt")
    end
    end
    else
    -- Bereken de eindpositie op basis van de maximale laserafstand
    hitPosition = tool.Handle.Position + directionVector
    end

Nu zou de laser blaster Speler geraakt naar het Output venster moeten afdrukken elke keer dat de raycast-operatie een andere speler raakt.

Test met meerdere spelers

Twee spelers zijn nodig om te testen of de wapenraycast andere spelers vindt, dus je moet een lokale server starten. Zie multi‑client simulatie voor instructies.

Test op één client het schieten op de andere speler met het wapen door op hen te klikken. "Speler geraakt" zou in de output moeten worden weergegeven elke keer dat een speler wordt geraakt.

Vind de laserpositie

De blaster moet een rode lichtstraal op zijn doel afvuren. De functie hiervoor zal binnen een ModuleScript zijn, zodat deze later in andere scripts kan worden hergebruikt. Eerst moet het script de positie vinden waar de laserstraal moet worden weergegeven.

  1. Maak een ModuleScript genaamd LaserRenderer, geparent aan StarterPlayerScripts onder StarterPlayer.

  2. Open het script en hernoem de module-tabel naar de naam van het script LaserRenderer.

  3. Declareer een variabele genaamd SHOT_DURATION met een waarde van 0.15. Dit zal de tijd (in seconden) zijn dat de laser zichtbaar is.

  4. Maak een functie van LaserRenderer genaamd createLaser met twee parameters genaamd toolHandle en endPosition.

    local LaserRenderer = {}
    local SHOT_DURATION = 0.15 -- Tijd dat de laser zichtbaar is
    -- Maak een laserstraal van een startpositie naar een eindpositie
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. Declareer een variabele genaamd startPosition en stel de Position eigenschap van toolHandle in als waarde. Dit zal de positie zijn van de laser blaster van de speler.

  6. Declareer een variabele genaamd laserDistance en trek endPosition van startPosition af om het verschil tussen de twee vectoren te vinden. Gebruik de Magnitude eigenschap hiervan om de lengte van de laserstraal te krijgen.

    function LaserRenderer.createLaser(toolHandle, endPosition)
    local startPosition = toolHandle.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    end
  7. Declareer een laserCFrame variabele om de positie en oriëntatie van de laserstraal op te slaan. De positie moet het middenpunt zijn van het begin en het einde van de straal. Gebruik CFrame.lookAt om een nieuwe CFrame te creëren die zich op startPosition bevindt en naar endPosition kijkt. Vermenigvuldig dit met een nieuwe CFrame met een Z-as waarde van de helft van de negatieve laserDistance om het middenpunt te krijgen.

    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)
    end

Maak het laseronderdeel

Nu je weet waar je een laserstraal moet maken, moet je de straal zelf toevoegen. Dit kan eenvoudig worden gedaan met een Neon onderdeel.

  1. Declareer een variabele laserPart en ken deze een nieuwe Part instantie toe.

  2. Stel de volgende eigenschappen van laserPart in:

    1. Size: Vector3.new(0.2, 0.2, laserDistance)
    2. CFrame: laserCFrame
    3. Anchored: true
    4. CanCollide: false
    5. Color: Color3.fromRGB(225, 0, 0) (een sterke rode kleur)
    6. Material: Enum.Material.Neon
  3. Parent laserPart naar Workspace.

  4. Voeg het onderdeel toe aan de Debris service zodat het wordt verwijderd na het aantal seconden in de SHOT_DURATION variabele.

    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(225, 0, 0)
    laserPart.Material = Enum.Material.Neon
    laserPart.Parent = workspace
    -- Voeg de laserstraal toe aan de Debris service om te worden verwijderd en opgeruimd
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

Nu is de functie om de laserstraal weer te geven compleet, deze kan worden aangeroepen door de ToolController.

  1. Bovenaan het ToolController script, declareer een variabele genaamd LaserRenderer en vereis de LaserRenderer ModuleScript die zich in PlayerScripts bevindt.

    local UserInputService = game:GetService("UserInputService")
    local Players = game:GetService("Players")
    local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
    local tool = script.Parent
  2. Aan het einde van de fireWeapon functie, roep de LaserRenderer createLaser functie aan met de handgreep van het gereedschap en hitPosition als argumenten.

    -- Bereken de eindpositie op basis van de maximale laserafstand
    hitPosition = tool.Handle.Position + directionVector
    end
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  3. Test het wapen door op de Afspelen knop te klikken. Een laserstraal zou zichtbaar moeten zijn tussen het wapen en de muis wanneer het gereedschap wordt geactiveerd.

Controleer de vuursnelheid van het wapen

Wapens hebben een vertraging tussen elke schot nodig om te voorkomen dat spelers in een korte tijd te veel schade toebrengen. Dit kan worden gecontroleerd door te kijken of er voldoende tijd is verstreken sinds de speler voor het laatst heeft geschoten.

  1. Declareer een variabele bovenaan de ToolController genaamd FIRE_RATE. Dit zal de minimale tijd tussen elk schot zijn. Geef het een waarde naar keuze; dit voorbeeld gebruikt 0.3 seconden.

  2. Declareer een andere variabele eronder genaamd timeOfPreviousShot met een waarde van 0. Dit slaat de laatste tijd op dat de speler heeft geschoten en zal worden bijgewerkt met elk schot.

    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 300
    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
  3. Maak een functie genaamd canShootWeapon zonder parameters. Deze functie zal kijken hoeveel tijd er is verstreken sinds het vorige schot en true of false retourneren.

    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
    -- Controleer of er voldoende tijd is verstreken sinds het vorige schot
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. Declareer binnen de functie een variabele genaamd currentTime; ken deze de waarde toe van het aanroepen van de tick() functie. Dit retourneert hoeveel tijd er is verstreken, in seconden, sinds 1 januari 1970 (een willekeurige datum die veel wordt gebruikt om tijd te berekenen).

  5. Trek timeOfPreviousShot af van currentTime en retourneer false als het resultaat kleiner is dan FIRE_RATE; anders retourneer true.

    -- Controleer of er voldoende tijd is verstreken sinds het vorige schot
    local function canShootWeapon()
    local currentTime = tick()
    if currentTime - timeOfPreviousShot < FIRE_RATE then
    return false
    end
    return true
    end
  6. Aan het einde van de fireWeapon functie, werk timeOfPreviousShot bij elke keer dat het wapen wordt afgevuurd met behulp van tick.

    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. Binnen de toolActivated functie, maak een if statement en roep canShootWeapon aan om te controleren of het wapen kan worden afgevuurd.

    local function toolActivated()
    if canShootWeapon() then
    tool.Handle.Activate:Play()
    fireWeapon()
    end
    end

Wanneer je de blaster test, zou je moeten merken dat, ongeacht hoe snel je klikt, er altijd een korte vertraging van 0.3 seconden tussen elk schot zal zijn.

Schade toebrengen aan de speler

Clients kunnen andere clients niet direct schade toebrengen; de server moet verantwoordelijk zijn voor het toebrengen van schade wanneer een speler wordt geraakt.

Clients kunnen een RemoteEvent gebruiken om de server te vertellen dat een karakter is geraakt. Deze moeten worden opgeslagen in ReplicatedStorage, waar ze zichtbaar zijn voor zowel client als server.

  1. Maak een Map in ReplicatedStorage genaamd Events.

  2. Voeg een RemoteEvent toe aan de Events-map en noem deze DamageCharacter.

  3. In ToolController, maak variabelen aan het begin van het script voor ReplicatedStorage en de Events-map.

    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
  4. Vervang de "Speler geraakt" print statement in fireWeapon door een regel van Luau om het DamageCharacter remote event af te vuren met de characterModel variabele als argument.

    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
    -- Bereken de eindpositie op basis van de maximale laserafstand
    hitPosition = tool.Handle.Position + directionVector
    end

De server moet schade toebrengen aan de speler die is geraakt wanneer het evenement wordt afgevuurd.

  1. Voeg een Script toe aan ServerScriptService en noem deze ServerLaserManager.

  2. Declareer een variabele genaamd LASER_DAMAGE en stel deze in op 10, of een waarde naar keuze.

  3. Maak een functie genaamd damageCharacter met twee parameters genaamd playerFired en characterToDamage.

  4. Binnen de functie, vind de Humanoid van het karakter en trek LASER_DAMAGE van zijn gezondheid af.

  5. Verbind de damageCharacter functie met het DamageCharacter remote event in de Events-map.

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    function damageCharacter(playerFired, characterToDamage)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- Verwijder gezondheid van karakter
    humanoid.Health -= LASER_DAMAGE
    end
    end
    -- Verbind evenementen met de juiste functies
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. Test de blaster met 2 spelers door een lokale server te starten. Wanneer je de andere speler schiet, zal hun gezondheid afnemen met het aantal dat is toegewezen aan LASER_DAMAGE.

Render de laserstralen van andere spelers

Momenteel wordt de laserstraal gemaakt door de client die het wapen afvuurt, zodat alleen zij de laserstraal kunnen zien.

Als de laserstraal op de server zou worden gemaakt, zou iedereen deze kunnen zien. Er zou echter een kleine vertraging zijn tussen de client die het wapen afvuurt en de server die de informatie over het schot ontvangt. Dit zou betekenen dat de client die het wapen afvuurt een vertraging zou zien tussen wanneer ze het wapen activeren en wanneer ze de laserstraal zien; het wapen zou daardoor traag aanvoelen.

Om dit probleem op te lossen, zal elke client zijn eigen laserstralen maken. Dit betekent dat de client die het wapen afvuurt de laserstraal onmiddellijk zal zien. Andere clients zullen een kleine vertraging ervaren tussen wanneer een andere speler schiet en een straal verschijnt. Dit is het beste scenario: er is geen manier om de laser van de ene client sneller naar andere clients te communiceren.

Shooter's client

Eerst moet de client de server vertellen dat hij een laser heeft afgevuurd en de eindpositie doorgeven.

  1. Voeg een RemoteEvent toe aan de Events-map in ReplicatedStorage en noem deze LaserFired.

  2. Zoek de fireWeapon functie in het ToolController script. Aan het einde van de functie, vuurt het LaserFired remote event af met hitPosition als argument.

    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    eventsFolder.LaserFired:FireServer(hitPosition)
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end

De server

De server moet nu het evenement ontvangen dat de client heeft geschoten en alle clients vertellen wat de start- en eindpositie van de laserstraal is, zodat ze deze ook kunnen weergeven.

  1. In het ServerLaserManager script, maak een functie genaamd playerFiredLaser boven de damageCharacter functie met twee parameters genaamd playerFired en endPosition.

  2. Verbind de functie met het LaserFired remote event.

    -- Meld alle clients dat er een laser is afgevuurd, zodat ze de laser kunnen weergeven
    local function playerFiredLaser(playerFired, endPosition)
    end
    -- Verbind evenementen met de juiste functies
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
    eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

De server heeft de startpositie van de laser nodig. Dit kan van de client worden verzonden, maar het is het beste om de client waar mogelijk niet te vertrouwen. De positie van de handgreep van het wapen van het karakter zal de startpositie zijn, zodat de server deze daar kan vinden.

  1. Maak een functie getPlayerToolHandle boven de playerFiredLaser functie met een parameter genaamd player.

  2. Gebruik de volgende code om het wapen van de speler te zoeken en het handgreepobject terug te geven.

    local LASER_DAMAGE = 10
    -- Vind de handgreep van het gereedschap dat de speler vasthoudt
    local function getPlayerToolHandle(player)
    local weapon = player.Character:FindFirstChildOfClass("Tool")
    if weapon then
    return weapon:FindFirstChild("Handle")
    end
    end
    -- Meld alle clients dat er een laser is afgevuurd, zodat ze de laser kunnen weergeven
    local function playerFiredLaser(playerFired, endPosition)

De server kan nu FireAllClients aanroepen op het LaserFired remote event om de informatie die nodig is om de laser weer te geven naar de clients te sturen. Dit omvat de speler die de laser heeft afgevuurd (zodat de client voor die speler de laser niet twee keer weergeeft), de handgreep van de blaster (die als startpositie voor de laser fungeert) en de eindpositie van de laser.

  1. In de playerFiredLaser functie, roep de getPlayerToolHandle functie aan met playerFired als argument en ken de waarde toe aan een variabele genaamd toolHandle.

  2. Als toolHandle bestaat, vuurt het LaserFired evenement voor alle clients af met playerFired, toolHandle en endPosition als argumenten.

    -- Meld alle clients dat er een laser is afgevuurd, zodat ze de laser kunnen weergeven
    local function playerFiredLaser(playerFired, endPosition)
    local toolHandle = getPlayerToolHandle(playerFired)
    if toolHandle then
    eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
    end
    end

Render op de clients

Nu FireAllClients is aangeroepen, zal elke client een evenement van de server ontvangen om een laserstraal weer te geven. Elke client kan de LaserRenderer module van eerder hergebruiken om de laserstraal weer te geven met behulp van de positie van het gereedschap en de eindpositiewaarde die door de server is verzonden. De speler die de laserstraal heeft afgevuurd, moet dit evenement negeren, anders zien ze 2 lasers.

  1. Maak een LocalScript in StarterPlayerScripts genaamd ClientLaserManager.

  2. Vereis binnen het script de LaserRenderer module.

  3. Maak een functie genaamd createPlayerLaser met de parameters playerWhoShot, toolHandle en endPosition.

  4. Verbind de functie met het LaserFired remote event in de Events-map.

  5. In de functie, gebruik een if statement om te controleren of playerWhoShot niet gelijk is aan de LocalPlayer.

  6. Binnen de if-statement, roep de createLaser functie van de LaserRenderer module aan met toolHandle en endPosition als argumenten.

    local Players = game:GetService("Players")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))
    local eventsFolder = ReplicatedStorage.Events
    -- Geef de laser van een andere speler weer
    local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
    if playerWhoShot ~= Players.LocalPlayer then
    LaserRenderer.createLaser(toolHandle, endPosition)
    end
    end
    eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
  7. Test de blaster met 2 spelers door een lokale server te starten. Positioneer elke client aan verschillende zijden van je monitor, zodat je beide vensters tegelijk kunt zien. Wanneer je op een client schiet, zou je de laser op de andere client moeten zien.

Geluidseffecten

Het schietgeluidseffect wordt momenteel alleen afgespeeld op de client die het projectiel afvuurt. Je moet de code verplaatsen om het geluid af te spelen, zodat andere spelers het ook kunnen horen.

  1. In het ToolController script, navigeer naar de toolActivated functie en verwijder de regel die het Activeren-geluid afspeelt.

    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. Aan het einde van de createLaser functie in LaserRenderer, declareer een variabele genaamd shootingSound en gebruik de FindFirstChild() methode van toolHandle om te controleren op het Activate geluid.

  3. Gebruik een if statement om te controleren of shootingSound bestaat; als dat zo is, roep dan zijn Play() functie aan.

    laserPart.Parent = workspace
    -- Voeg de laserstraal toe aan de Debris service om te worden verwijderd en opgeruimd
    Debris:AddItem(laserPart, SHOT_DURATION)
    -- Speel het schietgeluid van het wapen af
    local shootingSound = toolHandle:FindFirstChild("Activate")
    if shootingSound then
    shootingSound:Play()
    end
    end

Beveilig remotes met validatie

Als de server geen gegevens controleert van binnenkomende verzoeken, kan een hacker remote functies en evenementen misbruiken en deze gebruiken om valse waarden naar de server te sturen. Het is belangrijk om server-side validatie te gebruiken om dit te voorkomen.

In zijn huidige vorm is het DamageCharacter remote event zeer kwetsbaar voor aanvallen. Hackers kunnen dit evenement gebruiken om elke speler te beschadigen die ze willen zonder hen te schieten.

Validatie is het proces van het controleren of de waarden die naar de server worden verzonden realistisch zijn. In dit geval moet de server:

  • Controleren of de afstand tussen de speler en de positie die door de laser is geraakt binnen een bepaalde grens ligt.
  • Raycasten tussen het wapen dat de laser heeft afgevuurd en de positie die is geraakt om ervoor te zorgen dat het schot mogelijk was en niet door muren ging.

Client

De client moet de server de positie doorgeven die is geraakt door de raycast, zodat deze kan controleren of de afstand realistisch is.

  1. In ToolController, navigeer naar de regel waar het DamageCharacter remote event wordt afgevuurd in de fireWeapon functie.

  2. Voeg hitPosition als argument toe.

    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    eventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)
    end
    end

Server

De client verzendt nu een extra parameter via het DamageCharacter remote event, dus de ServerLaserManager moet worden aangepast om deze te accepteren.

  1. In het ServerLaserManager script, voeg een hitPosition parameter toe aan de damageCharacter functie.

    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- Verwijder gezondheid van karakter
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. Onder de getPlayerToolHandle functie, maak een functie genaamd isHitValid met drie parameters: playerFired, characterToDamage en hitPosition.

    end
    local function isHitValid(playerFired, characterToDamage, hitPosition)
    end

De eerste controle zal de afstand zijn tussen de hitpositie en het karakter dat is geraakt.

  1. Declareer een variabele genaamd MAX_HIT_PROXIMITY bovenaan het script en ken deze een waarde van 10 toe. Dit zal de maximale afstand zijn die is toegestaan tussen de hit en het karakter. Een tolerantie is nodig omdat het karakter mogelijk iets is verschoven sinds de client het evenement heeft afgevuurd.

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    local MAX_HIT_PROXIMITY = 10
  2. In de isHitValid functie, bereken de afstand tussen het karakter en de hitpositie. Als de afstand groter is dan MAX_HIT_PROXIMITY, retourneer dan false.

    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Valideer de afstand tussen het karakter dat is geraakt en de hitpositie
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > MAX_HIT_PROXIMITY then
    return false
    end
    end

De tweede controle zal een raycast tussen het afgevuurde wapen en de hitpositie omvatten. Als de raycast een object retourneert dat niet het karakter is, kun je aannemen dat het schot niet geldig was, aangezien er iets de schot heeft geblokkeerd.

  1. Kopieer de onderstaande code om deze controle uit te voeren. Retourneer true aan het einde van de functie: als het het einde bereikt, zijn alle controles geslaagd.

    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Valideer de afstand tussen het karakter dat is geraakt en de hitpositie
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 then
    return false
    end
    -- Controleer of er door muren wordt geschoten
    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)
    -- Als er een instantie is geraakt die niet het karakter is, negeer dan het schot
    if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
    return false
    end
    end
    return true
    end
  2. Declareer een variabele in de damageCharacter functie genaamd validShot. Ken deze de waarde toe van een aanroep naar de isHitValid functie met drie argumenten: playerFired, characterToDamage en hitPosition.

  3. In de onderstaande if-statement, voeg een en operator toe om te controleren of validShot waar is.

    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
    if humanoid and validShot then
    -- Verwijder gezondheid van karakter
    humanoid.Health -= LASER_DAMAGE
    end
    end

Nu is het damageCharacter remote event veiliger en zal het de meeste spelers voorkomen om het te misbruiken. Merk op dat sommige kwaadwillende spelers vaak manieren vinden om om validatie heen te werken; het veilig houden van remote events is een voortdurende inspanning.

Je laser blaster is nu compleet, met een basis hitdetectiesysteem dat gebruikmaakt van raycasting. Probeer de Detecting User Input tutorial om te ontdekken hoe je een herlaadactie aan je laser blaster kunt toevoegen, of maak een leuke map en probeer je laser blaster uit met andere spelers!

Eindcode

ToolController

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
-- Controleer of er voldoende tijd is verstreken sinds het vorige schot
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()
-- Maak een straal van de 2D muislocatie
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- De eenheidsrichtingvector van de straal vermenigvuldigd met een maximale afstand
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast van de oorsprong van de straal naar zijn richting
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Retourneer het 3D-punt van intersectie
return raycastResult.Position
else
-- Geen object geraakt, dus bereken de positie aan het einde van de straal
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Bereken een genormaliseerde richtingvector en vermenigvuldig met laserafstand
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- De richting om het wapen af te vuren, vermenigvuldigd met een maximale afstand
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Negeer de karakter van de speler om te voorkomen dat ze zichzelf beschadigen
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Controleer of er objecten zijn geraakt tussen de start- en eindpositie
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- De instantie die is geraakt, zal een kind zijn van een karaktermodel
-- Als er een humanoïde in het model wordt gevonden, is het waarschijnlijk een karakter van een speler
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
-- Bereken de eindpositie op basis van de maximale laserafstand
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)

LaserRenderer

local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Tijd dat de laser zichtbaar is
-- Maak een laserstraal van een startpositie naar een eindpositie
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
-- Voeg de laserstraal toe aan de Debris service om te worden verwijderd en opgeruimd
Debris:AddItem(laserPart, SHOT_DURATION)
-- Speel het schietgeluid van het wapen af
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
-- Vind de handgreep van het gereedschap dat de speler vasthoudt
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)
-- Valideer de afstand tussen het karakter dat is geraakt en de hitpositie
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Controleer of er door muren wordt geschoten
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)
-- Als er een instantie is geraakt die niet het karakter is, negeer dan het schot
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Meld alle clients dat er een laser is afgevuurd, zodat ze de laser kunnen weergeven
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
-- Verwijder gezondheid van karakter
humanoid.Health -= LASER_DAMAGE
end
end
-- Verbind evenementen met de juiste functies
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

ClientLaserManager

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- Geef de laser van een andere speler weer
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
©2026 Roblox Corporation. Roblox, het Roblox-logo en Powering Imagination behoren tot onze geregistreerde en niet-geregistreerde handelsmerken in de VS en andere landen.