Commencer
Tutoriels intermédiaires

Détection des impacts avec des lasers

*Ce contenu est traduit en utilisant l'IA (Beta) et peut contenir des erreurs. Pour consulter cette page en anglais, clique ici.

Dans ce tutoriel, vous apprendrez comment lancer un laser depuis le blaster dans Créer des outils pour les joueurs et détecter s'il touche un joueur.

Raycasting pour trouver des collisions

Raycasting crée un rayon invisible à partir d'une position de départ vers une direction donnée avec une longueur définie. Si le rayon entre en collision avec des objets ou le terrain sur son chemin, il renverra des informations sur la collision, telles que la position et l'objet avec lequel il est entré en collision.

Raycast de A vers B frappant un mur

Trouver la localisation de la souris

Avant qu'un laser ne puisse être tiré, vous devez d'abord savoir où le joueur vise. Cela peut être trouvé en raycastant depuis la localisation 2D de la souris du joueur à l'écran directement vers l'avant depuis la caméra dans le monde 3D. Le rayon entrera en collision avec ce que le joueur vise avec la souris.

  1. Ouvrez le script ToolController à l'intérieur de l'outil Blaster depuis Créer des outils pour les joueurs. Si vous n'avez pas encore terminé ce tutoriel, vous pouvez télécharger le modèle Blaster et l'insérer dans StarterPack.

  2. En haut du script, déclarez une constante nommée MAX_MOUSE_DISTANCE avec une valeur de 1000.

  3. Créez une fonction appelée 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
    -- Connectez les événements aux fonctions appropriées
    tool.Equipped:Connect(toolEquipped)
    tool.Activated:Connect(toolActivated)
  4. Utilisez la fonction GetMouseLocation de UserInputService pour obtenir la localisation 2D de la souris du joueur à l'écran. Assignez cela à une variable nommée mouseLocation.


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

Maintenant que la localisation 2D de la souris est connue, ses propriétés X et Y peuvent être utilisées comme paramètres pour la fonction Camera:ViewportPointToRay(), qui crée un Ray de l'écran vers le monde 3D.

  1. Utilisez les propriétés X et Y de mouseLocation comme arguments pour la fonction ViewportPointToRay(). Assignez ceci à une variable nommée screenToWorldRay.


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Créer un rayon à partir de la localisation 2D de la souris
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    end

Il est temps d'utiliser la fonction Raycast pour vérifier si le rayon touche un objet. Cela nécessite une position de départ et un vecteur de direction : dans cet exemple, vous utiliserez les propriétés d'origine et de direction de screenToWorldRay.

La longueur du vecteur de direction détermine jusqu'où le rayon voyagera. Le rayon doit être aussi long que la MAX_MOUSE_DISTANCE, vous devrez donc multiplier le vecteur de direction par MAX_MOUSE_DISTANCE.

  1. Déclarez une variable nommée directionVector et assignez-lui la valeur de screenToWorldRay.Direction multipliée par MAX_MOUSE_DISTANCE.


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Créer un rayon à partir de la localisation 2D de la souris
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Le vecteur de direction unitaire du rayon multiplié par une distance maximale
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
  2. Appelez la fonction Raycast de workspace, en passant la propriété Origin de screenToWorldRay comme premier argument et directionVector comme second. Assignez ceci à une variable nommée raycastResult.


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Créer un rayon à partir de la localisation 2D de la souris
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Le vecteur de direction unitaire du rayon multiplié par une distance maximale
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
    -- Raycast depuis l'origine du rayon vers sa direction
    local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)

Informations sur les collisions

Si l'opération de raycast trouve un objet touché par le rayon, elle renverra un RaycastResult, qui contient des informations sur la collision entre le rayon et l'objet.

Propriété RaycastResultDescription
InstanceLe BasePart ou la cellule Terrain que le rayon a intersecté.
PositionOù l'intersection a eu lieu ; généralement un point directement sur la surface d'une partie ou du terrain.
MatériauLe matériau au point de collision.
NormalLe vecteur normal de la face intersequée. Cela peut être utilisé pour déterminer dans quelle direction la face est orientée.

La propriété Position sera la position de l'objet sur lequel la souris survole. Si la souris n'est pas survolée au-dessus d'un objet à une distance de MAX_MOUSE_DISTANCE, raycastResult sera nil.

  1. Créez une instruction if pour vérifier si raycastResult existe.

  2. Si raycastResult a une valeur, renvoyez sa propriété Position.

  3. Si raycastResult est nil, alors trouvez la fin du raycast. Calculez la position 3D de la souris en ajoutant screenToWorldRay.Origin et directionVector ensemble.


local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Créer un rayon à partir de la localisation 2D de la souris
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Le vecteur de direction unitaire du rayon multiplié par une distance maximale
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast depuis l'origine du rayon vers sa direction
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Retourne le point 3D d'intersection
return raycastResult.Position
else
-- Aucun objet n'a été touché, calculez donc la position à la fin du rayon
return screenToWorldRay.Origin + directionVector
end
end

Tirer vers la cible

Maintenant que la position 3D de la souris est connue, elle peut être utilisée comme une position cible pour tirer un laser. Un deuxième rayon peut être tiré entre la position d'arme du joueur et la position cible à l'aide de la fonction Raycast.

  1. Déclarez une constante appelée MAX_LASER_DISTANCE en haut du script et assignez-lui 500, ou votre portée choisie pour le blaster laser.


    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  2. Créez une fonction appelée fireWeapon sous la fonction getWorldMousePosition.

  3. Appelez getWorldMousePosition et assignez le résultat à une variable nommée mousePosition. Ce sera la position cible pour le raycast.


    -- Aucun objet n'a été touché, calculez donc la position à la fin du rayon
    return screenToWorldRay.Origin + directionVector
    end
    end
    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end

Cette fois, le vecteur de direction pour la fonction de raycast représentera la direction de la position de l'outil du joueur à la position cible.

  1. Déclarez une variable nommée targetDirection et calculez le vecteur de direction en soustrayant la position de l'outil de mouseLocation.

  2. Normalisez le vecteur en utilisant sa propriété Unit. Cela lui donne une magnitude de 1, ce qui facilite la multiplication par une longueur par la suite.


    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    -- Calculez un vecteur de direction normalisé et multipliez-le par la distance du laser
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. Déclarez une variable nommée directionVector et assignez-lui le targetDirection multiplié par MAX_LASER_DISTANCE.


    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    -- La direction pour tirer l'arme, multipliée par une distance maximale
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

Un objet RaycastParams peut être utilisé pour stocker des paramètres supplémentaires pour la fonction raycast. Il sera utilisé dans votre blaster laser pour s'assurer que le raycast ne touche pas accidentellement le joueur tirant l'arme. Toutes les parties incluses dans la propriété FilterDescendantsInstances d'un objet RaycastParams seront ignorées dans le raycast.

  1. Continuez la fonction fireWeapon et déclarez une variable appelée weaponRaycastParams. Assignez-lui un nouvel objet RaycastParams.

  2. Créez une table contenant le personnage local du joueur et assignez-la à la propriété weaponRaycastParams.FilterDescendantsInstances.

  3. Raycastez depuis la position du manche de l'outil du joueur, dans une direction vers le directionVector. N'oubliez pas d'ajouter weaponRaycastParams comme argument cette fois. Assignez ceci à une variable nommée 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()
-- Calculez un vecteur de direction normalisé et multipliez-le par la distance du laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direction pour tirer l'arme multipliée par une distance maximale
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignorez le personnage du joueur pour éviter qu'il ne se blesse lui-même
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end

Enfin, vous devrez vérifier que l'opération de raycast a renvoyé une valeur. Si une valeur est renvoyée, un objet a été touché par le rayon et un laser peut être créé entre l'arme et l'emplacement d'impact. Si rien n'a été renvoyé, la position finale doit être calculée afin de créer le laser.

  1. Déclarez une variable vide nommée hitPosition.

  2. Utilisez une instruction if pour vérifier si weaponRaycastResult a une valeur. Si un objet a été touché, assignez weaponRaycastResult.Position à hitPosition.


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Vérifiez si des objets ont été touchés entre la position de départ et la position finale
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. Si weaponRaycastResult n'a pas de valeur, calculez la position de fin du raycast en ajoutant la position du manche de l'outil avec le directionVector. Assignez ceci à hitPosition.


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Vérifiez si des objets ont été touchés entre la position de départ et la position finale
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    else
    -- Calculez la position finale en fonction de la distance maximale du laser
    hitPosition = tool.Handle.Position + directionVector
    end
    end
  4. Accédez à la fonction toolActivated et appelez la fonction fireWeapon afin que le laser tire chaque fois que l'outil est activé.


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

Vérifiez l'objet touché

Pour savoir si l'objet touché par le laser fait partie du personnage d'un joueur ou s'il s'agit simplement d'un morceau de décor, vous devrez rechercher un Humanoid, car chaque personnage en a un.

Tout d'abord, vous devrez trouver le modèle du personnage. Si une partie du personnage a été touchée, vous ne pouvez pas présumer que le parent de l'objet touché serait le personnage. Le laser pourrait avoir touché une partie du corps, un accessoire ou un outil, qui sont tous situés dans différentes parties de la hiérarchie du personnage.

Vous pouvez utiliser FindFirstAncestorOfClass pour trouver un ancêtre modèle de personnage de l'objet touché par le laser, s'il en existe un. Si vous trouvez un modèle et qu'il contient un humanoïde, dans la plupart des cas, vous pouvez supposer qu'il s'agit d'un personnage.

  1. Ajoutez le code surligné ci-dessous à l'instruction if de weaponRaycastResult pour vérifier si un personnage a été touché.


    -- Vérifiez si des objets ont été touchés entre la position de départ et la position finale
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    -- L'instance touchée sera un enfant d'un modèle de personnage
    -- Si un humanoïde est trouvé dans le modèle, alors il s'agit probablement du personnage d'un joueur
    local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    print("Joueur touché")
    end
    end
    else
    -- Calculez la position finale en fonction de la distance maximale du laser
    hitPosition = tool.Handle.Position + directionVector
    end

Maintenant, le blaster laser devrait imprimer Joueur touché dans la fenêtre Output chaque fois que l'opération de raycast touche un autre joueur.

Testez avec plusieurs joueurs

Deux joueurs sont nécessaires pour tester si le raycast de l'arme trouve d'autres joueurs, donc vous devez démarrer un serveur local. Voir simulation multi-client pour les instructions.

Sur un client, testez le tir sur l'autre joueur avec l'arme en cliquant sur eux. "Joueur touché" devrait s'afficher dans la sortie chaque fois qu'un joueur est tiré.

Trouver la position du laser

Le blaster devrait tirer un faisceau de lumière rouge vers sa cible. La fonction pour cela se trouvera à l'intérieur d'un ModuleScript afin qu'elle puisse être réutilisée dans d'autres scripts plus tard. Tout d'abord, le script devra trouver la position où le faisceau laser doit être rendu.

  1. Créez un ModuleScript nommé LaserRenderer, parenté à StarterPlayerScripts sous StarterPlayer.

  2. Ouvrez le script et renommez la table du module au nom du script LaserRenderer.

  3. Déclarez une variable nommée SHOT_DURATION avec une valeur de 0.15. Cela sera la durée (en secondes) pendant laquelle le laser est visible.

  4. Créez une fonction de LaserRenderer nommée createLaser avec deux paramètres appelés toolHandle et endPosition.


    local LaserRenderer = {}
    local SHOT_DURATION = 0.15 -- Temps pendant lequel le laser est visible
    -- Créez un faisceau laser d'une position de départ à une position de fin
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. Déclarez une variable nommée startPosition et définissez la propriété Position de toolHandle comme sa valeur. Ce sera la position du blaster laser du joueur.

  6. Déclarez une variable nommée laserDistance et soustrayez endPosition de startPosition pour trouver la différence entre les deux vecteurs. Utilisez la propriété Magnitude pour obtenir la longueur du faisceau laser.


    function LaserRenderer.createLaser(toolHandle, endPosition)
    local startPosition = toolHandle.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    end
  7. Déclarez une variable laserCFrame pour stocker la position et l'orientation du faisceau laser. La position doit être le point médian du début et de la fin du faisceau. Utilisez CFrame.lookAt pour créer un nouveau CFrame situé à startPosition et orienté vers endPosition. Multipliez ceci par une nouvelle CFrame avec une valeur d'axe Z de la moitié de laserDistance négatif pour obtenir le point médian.


    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

Créer la partie du laser

Maintenant que vous savez où créer un faisceau laser, vous devez ajouter le faisceau lui-même. Cela peut être fait facilement avec une pièce en Neon.

  1. Déclarez une variable laserPart et assignez-lui une nouvelle instance de Part.

  2. Réglez les propriétés suivantes de laserPart :

    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) (une couleur rouge vif)
    6. Material : Enum.Material.Neon
  3. Parent laserPart dans Workspace.

  4. Ajoutez la pièce au service Debris afin qu'elle soit supprimée après le nombre de secondes dans la variable SHOT_DURATION.


    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
    -- Ajoutez le faisceau laser au service Debris pour qu'il soit supprimé et nettoyé
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

Maintenant que la fonction pour rendre le faisceau laser est complète, elle peut être appelée par le ToolController.

  1. En haut du script ToolController, déclarez une variable nommée LaserRenderer et exigez le ModuleScript LaserRenderer situé dans PlayerScripts.


    local UserInputService = game:GetService("UserInputService")
    local Players = game:GetService("Players")
    local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
    local tool = script.Parent
  2. Au bas de la fonction fireWeapon, appelez la fonction createLaser de LaserRenderer en utilisant le manche de l'outil et hitPosition comme arguments.


    -- Calculez la position finale en fonction de la distance maximale du laser
    hitPosition = tool.Handle.Position + directionVector
    end
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  3. Testez l'arme en cliquant sur le bouton Play. Un faisceau laser devrait être visible entre l'arme et la souris lorsque l'outil est activé.

Contrôlez le taux de tir de l'arme

Les armes ont besoin d'un délai entre chaque tir pour empêcher les joueurs de causer trop de dégâts en peu de temps. Cela peut être contrôlé en vérifiant si suffisamment de temps s'est écoulé depuis le dernier tir du joueur.

  1. Déclarez une variable en haut du ToolController appelée FIRE_RATE. Ce sera le minimum de temps entre chaque tir. Donnez-lui une valeur de votre choix ; cet exemple utilise 0.3 secondes.

  2. Déclarez une autre variable en dessous appelée timeOfPreviousShot avec une valeur de 0. Cela stocke le dernier moment où le joueur a tiré et sera mis à jour avec chaque tir.


    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 300
    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
  3. Créez une fonction appelée canShootWeapon sans paramètres. Cette fonction regardera combien de temps s'est écoulé depuis le tir précédent et renverra vrai ou faux.


    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
    -- Vérifier si suffisamment de temps s'est écoulé depuis le dernier tir
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. À l'intérieur de la fonction, déclarez une variable nommée currentTime ; assignez-lui le résultat de l'appel à la fonction tick(). Cela renvoie combien de temps s'est écoulé, en secondes, depuis le 1er janvier 1970 (une date arbitraire largement utilisée pour calculer le temps).

  5. Soustrayez timeOfPreviousShot de currentTime et renvoyez false si le résultat est inférieur à FIRE_RATE ; sinon, renvoyez true.


    -- Vérifier si suffisamment de temps s'est écoulé depuis le dernier tir
    local function canShootWeapon()
    local currentTime = tick()
    if currentTime - timeOfPreviousShot < FIRE_RATE then
    return false
    end
    return true
    end
  6. À la fin de la fonction fireWeapon, mettez à jour timeOfPreviousShot chaque fois que l'arme est tirée en utilisant tick.


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. À l'intérieur de la fonction toolActivated, créez une instruction if et appelez canShootWeapon pour vérifier si l'arme peut être tirée.


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

Lorsque vous testez le blaster, vous devriez constater que peu importe la rapidité avec laquelle vous cliquez, il y aura toujours un court délai de 0,3 secondes entre chaque tir.

Infliger des dégâts au joueur

Les clients ne peuvent pas blesser directement d'autres clients ; le serveur doit être responsable de l'émission des blessures lorsque un joueur est touché.

Les clients peuvent utiliser un RemoteEvent pour dire au serveur qu'un personnage a été touché. Ceux-ci doivent être stockés dans ReplicatedStorage, où ils sont visibles tant pour le client que pour le serveur.

  1. Créez un Dossier dans ReplicatedStorage nommé Events.

  2. Insérez un RemoteEvent dans le dossier Events et nommez-le DamageCharacter.

  3. Dans ToolController, créez des variables au début du script pour ReplicatedStorage et le dossier Events.


    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. Remplacez l'instruction d'impression "Joueur touché" dans fireWeapon par une ligne de Luau pour tirer l'événement DamageCharacter avec la variable characterModel comme 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
    -- Calculez la position finale en fonction de la distance maximale du laser
    hitPosition = tool.Handle.Position + directionVector
    end

Le serveur doit infliger des dégâts au joueur qui a été touché lorsque l'événement est tiré.

  1. Insérez un Script dans ServerScriptService et nommez-le ServerLaserManager.

  2. Déclarez une variable nommée LASER_DAMAGE et assignez-la à 10, ou à une valeur de votre choix.

  3. Créez une fonction nommée damageCharacter avec deux paramètres appelés playerFired et characterToDamage.

  4. À l'intérieur de la fonction, trouvez l'Humanoid du personnage et soustrayez LASER_DAMAGE de sa santé.

  5. Connectez la fonction damageCharacter à l'événement DamageCharacter dans le dossier Events.


    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
    -- Retirer la santé du personnage
    humanoid.Health -= LASER_DAMAGE
    end
    end
    -- Connectez les événements aux fonctions appropriées
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. Testez le blaster avec 2 joueurs en démarrant un serveur local. Lorsque vous tirez sur l'autre joueur, sa santé diminuera du nombre assigné à LASER_DAMAGE.

Rendre les faisceaux laser d'autres joueurs

Actuellement, le faisceau laser est créé par le client tirant l'arme, donc seul celui-ci pourra voir le faisceau laser.

Si le faisceau laser était créé sur le serveur, tout le monde pourrait le voir. Cependant, il y aurait un petit délai entre le client tirant l'arme et le serveur recevant les informations concernant le tir. Cela signifierait que le client tirant l'arme verrait un délai entre le moment où il active l'arme et celui où il voit le faisceau laser ; l'arme semblerait laguer en conséquence.

Pour résoudre ce problème, chaque client créera ses propres faisceaux laser. Cela signifie que le client tirant l'arme verra instantanément le faisceau laser. Les autres clients subiront un petit délai entre le moment où un autre joueur tire et l'apparition d'un faisceau. C'est le meilleur scénario : il n'y a aucun moyen de communiquer le laser d'un client aux autres clients plus rapidement.

Client du tireur

Tout d'abord, le client doit informer le serveur qu'il a tiré un laser et fournir la position de fin.

  1. Insérez un RemoteEvent dans le dossier Events de ReplicatedStorage et nommez-le LaserFired.

  2. Localisez la fonction fireWeapon dans le script ToolController. À la fin de la fonction, tirez l'événement LaserFired en utilisant hitPosition comme argument.


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

Le serveur

Le serveur doit désormais recevoir l'événement que le client a tiré et dire à tous les clients la position de départ et de fin du faisceau laser afin qu'ils puissent également le rendre.

  1. Dans le script ServerLaserManager, créez une fonction nommée playerFiredLaser au-dessus de damageCharacter ayant deux paramètres appelés playerFired et endPosition.

  2. Connectez la fonction à l'événement LaserFired.


    -- Informez tous les clients qu'un laser a été tiré afin qu'ils puissent afficher le laser
    local function playerFiredLaser(playerFired, endPosition)
    end

    -- Connectez les événements aux fonctions appropriées
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
    eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

Le serveur a besoin de la position de départ du laser. Cela pourrait être envoyé par le client, mais il est préférable d'éviter de faire confiance au client autant que possible. La position de la poignée de l'arme du personnage sera la position de départ, donc le serveur peut la trouver à partir de là.

  1. Créez une fonction getPlayerToolHandle au-dessus de la fonction playerFiredLaser avec un paramètre appelé player.

  2. Utilisez le code suivant pour rechercher l'arme du joueur dans son personnage et retourner l'objet de la poignée.


    local LASER_DAMAGE = 10
    -- Trouver la poignée de l'outil que le joueur tient
    local function getPlayerToolHandle(player)
    local weapon = player.Character:FindFirstChildOfClass("Tool")
    if weapon then
    return weapon:FindFirstChild("Handle")
    end
    end
    -- Informez tous les clients qu'un laser a été tiré afin qu'ils puissent afficher le laser
    local function playerFiredLaser(playerFired, endPosition)

Le serveur peut maintenant appeler FireAllClients sur l'événement LaserFired pour envoyer les informations nécessaires pour rendre le laser aux clients. Cela inclut le joueur qui a tiré le laser (afin que le client pour ce joueur ne rende pas le laser deux fois), la poignée du blaster (qui agit comme une position de départ pour le laser) et la position de fin du laser.

  1. Dans la fonction playerFiredLaser, appelez la fonction getPlayerToolHandle avec playerFired comme argument et assignez la valeur à une variable nommée toolHandle.

  2. Si toolHandle existe, tirez l'événement LaserFired pour tous les clients en utilisant playerFired, toolHandle et endPosition comme arguments.


    -- Informez tous les clients qu'un laser a été tiré afin qu'ils puissent afficher le laser
    local function playerFiredLaser(playerFired, endPosition)
    local toolHandle = getPlayerToolHandle(playerFired)
    if toolHandle then
    eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
    end
    end

Rendre sur les clients

Maintenant que FireAllClients a été appelé, chaque client recevra un événement du serveur pour rendre un faisceau laser. Chaque client peut réutiliser le module LaserRenderer d'avant pour rendre le faisceau laser en utilisant la position de la poignée de l'outil et la valeur de position de fin envoyée par le serveur. Le joueur qui a tiré le faisceau laser au départ doit ignorer cet événement sinon il verra 2 lasers.

  1. Créez un LocalScript dans StarterPlayerScripts appelé ClientLaserManager.

  2. À l'intérieur du script, exigez le module LaserRenderer.

  3. Créez une fonction nommée createPlayerLaser avec les paramètres playerWhoShot, toolHandle et endPosition.

  4. Connectez la fonction à l'événement LaserFired dans le dossier Events.

  5. Dans la fonction, utilisez une instruction if pour vérifier si playerWhoShot n'est pas égal à LocalPlayer.

  6. À l'intérieur de l'instruction if, appelez la fonction createLaser à partir du module LaserRenderer en utilisant toolHandle et endPosition comme arguments.


    local Players = game:GetService("Players")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))
    local eventsFolder = ReplicatedStorage.Events
    -- Afficher le laser d'un autre joueur
    local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
    if playerWhoShot ~= Players.LocalPlayer then
    LaserRenderer.createLaser(toolHandle, endPosition)
    end
    end
    eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
  7. Testez le blaster avec 2 joueurs en démarrant un serveur local. Placez chaque client sur des côtés différents de votre moniteur afin que vous puissiez voir les deux fenêtres en même temps. Lorsque vous tirez d'un client, vous devriez voir le laser de l'autre client.

Effets sonores

L'effet sonore de tir se joue actuellement uniquement sur le client qui tire le projectile. Vous devrez déplacer le code pour jouer le son afin que les autres joueurs l'entendent également.

  1. Dans le script ToolController, accédez à la fonction toolActivated et supprimez la ligne qui joue le son Activate.


    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. Au bas de la fonction createLaser dans LaserRenderer, déclarez une variable nommée shootingSound et utilisez la méthode FindFirstChild() de toolHandle pour vérifier le son Activate.

  3. Utilisez une instruction if pour vérifier si shootingSound existe ; si c'est le cas, appelez sa fonction Play().


    laserPart.Parent = workspace
    -- Ajoutez le faisceau laser au service Debris pour qu'il soit supprimé et nettoyé
    Debris:AddItem(laserPart, SHOT_DURATION)
    -- Jouez le son de tir de l'arme
    local shootingSound = toolHandle:FindFirstChild("Activate")
    if shootingSound then
    shootingSound:Play()
    end
    end

Sécuriser les remotes à l'aide de la validation

Si le serveur ne vérifie pas les données des requêtes entrantes, un pirate peut abuser des fonctions et événements distants et les utiliser pour envoyer de fausses valeurs au serveur. Il est important d'utiliser la validation côté serveur pour empêcher cela.

Sous sa forme actuelle, l'événement distant DamageCharacter est très vulnérable à l'attaque. Les pirates pourraient utiliser cet événement pour blesser n'importe quel joueur sans leur tirer dessus.

La validation est le processus de vérification que les valeurs envoyées au serveur sont réalistes. Dans ce cas, le serveur devra :

  • Vérifier si la distance entre le joueur et la position touchée par le laser est dans une certaine limite.
  • Raycaster entre l'arme qui a tiré le laser et la position touchée pour s'assurer que le tir était possible et n'a pas traversé de murs.

Client

Le client doit envoyer au serveur la position touchée par le raycast afin qu'il puisse vérifier que la distance est réaliste.

  1. Dans ToolController, accédez à la ligne où l'événement DamageCharacter distant est tiré dans la fonction fireWeapon.

  2. Ajoutez hitPosition comme argument.


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

Serveur

Le client envoie désormais un paramètre supplémentaire via l'événement distant DamageCharacter, donc le ServerLaserManager doit être ajusté pour l'accepter.

  1. Dans le script ServerLaserManager, ajoutez un paramètre hitPosition à la fonction damageCharacter.


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- Retirez la santé du personnage
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. En dessous de la fonction getPlayerToolHandle, créez une fonction nommée isHitValid avec trois paramètres : playerFired, characterToDamage et hitPosition.


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

La première vérification sera la distance entre la position touchée et le personnage touché.

  1. Déclarez une variable nommée MAX_HIT_PROXIMITY en haut du script et assignez-lui une valeur de 10. Cela sera la distance maximale autorisée entre le coup touché et le personnage. Une tolérance est nécessaire car le personnage a pu légèrement bouger depuis que le client a tiré l'événement.


    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    local MAX_HIT_PROXIMITY = 10
  2. Dans la fonction isHitValid, calculez la distance entre le personnage et la position touchée. Si la distance est supérieure à MAX_HIT_PROXIMITY, alors renvoyez false.


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Valider la distance entre le personnage touché et la position touchée
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > MAX_HIT_PROXIMITY then
    return false
    end
    end

La deuxième vérification impliquera un raycast entre l'arme tirée et la position touchée. Si le raycast renvoie un objet qui n'est pas le personnage, vous pouvez supposer que le tir n'était pas valide puisque quelque chose bloquait le tir.

  1. Copiez le code ci-dessous pour effectuer cette vérification. Renvoie true à la fin de la fonction : s'il atteint la fin, toutes les vérifications ont été passées.


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Valider la distance entre le personnage touché et la position touchée
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 then
    return false
    end
    -- Vérifiez si vous tirez à travers des murs
    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 une instance a été touchée qui n'était pas le personnage, ignorez le tir
    if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
    return false
    end
    end
    return true
    end
  2. Déclarez une variable dans la fonction damageCharacter appelée validShot. Assignez-lui le résultat d'un appel à la fonction isHitValid avec trois arguments : playerFired, characterToDamage et hitPosition.

  3. Dans l'instruction if ci-dessous, ajoutez un opérateur et pour vérifier si validShot est true.


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
    if humanoid and validShot then
    -- Retirez la santé du personnage
    humanoid.Health -= LASER_DAMAGE
    end
    end

Maintenant, l'événement damageCharacter est plus sécurisé et empêchera la plupart des joueurs d'abuser de celui-ci. Notez que certains joueurs malveillants trouveront souvent des moyens de contourner la validation ; garder les événements distants sécurisés est un effort continu.

Votre blaster laser est maintenant complet, avec un système de détection d'impact basique utilisant le raycasting. Essayez le tutoriel Détecter l'entrée utilisateur pour découvrir comment vous pouvez ajouter une action de rechargement à votre blaster laser, ou créez une carte amusante et essayez votre blaster laser avec d'autres joueurs !

Code final

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
-- Vérifier si suffisamment de temps s'est écoulé depuis le dernier tir
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()
-- Créer un rayon à partir de la localisation 2D de la souris
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Le vecteur de direction unitaire du rayon multiplié par une distance maximale
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast depuis l'origine du rayon vers sa direction
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Retournez le point 3D d'intersection
return raycastResult.Position
else
-- Aucun objet n'a été touché, calculez donc la position à la fin du rayon
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Calculez un vecteur de direction normalisé et multipliez-le par la distance du laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direction pour tirer l'arme, multipliée par une distance maximale
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignorer le personnage du joueur pour éviter qu'il ne se blesse lui-même
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Vérifiez si des objets ont été touchés entre la position de départ et la position finale
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- L'instance touchée sera un enfant d'un modèle de personnage
-- Si un humanoïde est trouvé dans le modèle, alors il s'agit probablement du personnage d'un joueur
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
-- Calculez la position finale en fonction de la distance maximale du 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)

LaserRenderer


local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Temps pendant lequel le laser est visible
-- Créez un faisceau laser d'une position de départ à une position de fin
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
-- Ajoutez le faisceau laser au service Debris pour qu'il soit supprimé et nettoyé
Debris:AddItem(laserPart, SHOT_DURATION)
-- Jouez le son de tir de l'arme
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
-- Trouver la poignée de l'outil que le joueur tient
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)
-- Valider la distance entre le personnage touché et la position touchée
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Vérifiez si vous tirez à travers des murs
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 une instance a été touchée qui n'était pas le personnage, ignorez le tir
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Informez tous les clients qu'un laser a été tiré afin qu'ils puissent afficher le 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
-- Retirez la santé du personnage
humanoid.Health -= LASER_DAMAGE
end
end
-- Connectez les événements aux fonctions appropriées
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
-- Afficher le laser d'un autre joueur
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
©2026 Société Roblox. Roblox, le logo Roblox et Powering Imagination font partie de nos marques déposées aux États-Unis et dans d'autres pays.