Dans ce tutoriel, vous apprendrez à lancer un laser à partir du blaster en créer des outils de joueur et à détecter s'il frappe ou non un joueur.
Lancer de rayons 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 se heurte à des objets ou du terrain sur son chemin, il renvoie des informations sur la collision telles que la position et l'objet avec lequel il a collidé.

Trouver l'emplacement de la souris
Avant que le laser puisse être tiré, vous devez d'abord savoir où le joueur vise.Cela peut être trouvé en faisant un balayage à partir de la position de la souris 2D du joueur sur l'écran directement vers le monde du jeu.Le rayon va s'écraser sur tout ce que le joueur vise avec la souris.
Ouvrez le script Contrôleur d'outils à l'intérieur de l'outil Blaster à partir de Créer les outils du joueur.Si vous n'avez pas encore terminé ce tutoriel, vous pouvez télécharger le modèle Blaster et l'insérer dans StarterPack.
En haut du script, déclarez une constante nommée MAX_MOUSE_DISTANCE avec une valeur de 1000 .
Créez une fonction appelée 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-- Relier les événements à des fonctions appropriéestool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Utilisez la fonction GetMouseLocation de UserInputService pour obtenir la position de la souris 2D du joueur sur l'écran.Attribuez-le à une variable nommée mouseLocation .
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
Maintenant que l'emplacement de la souris 2D est connu, ses propriétés X et Y peuvent être utilisées comme paramètres pour la fonction >, qui crée un depuis l'écran dans le monde du jeu 3D.
Utilisez les propriétés X et Y de mouseLocation comme arguments pour la fonction ViewportPointToRay().Attribuez ceci à une variable nommée screenToWorldRay .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Créer un rayon à partir de la position de la souris 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
Il est temps d'utiliser la fonction Raycast pour vérifier si le rayon frappe un objet.Cela nécessite une position de départ et un force vectoriellede 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 se déplacera.Le rayon doit être aussi long que le MAX_MOUSE_DISTANCE, donc vous devrez multiplier le vecteur de direction par MAX_MOUSE_DISTANCE.
Déclarez une variable nommée directionVector et lui attribuez 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 position de la souris 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Le vecteur de direction d'unité du rayon multiplié par une distance maximalelocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEAppellez la fonction Raycast de l'espace de travail, en passant la propriété Origin de screenToWorldRay comme premier argument et directionVector comme deuxième.Attribuez ceci à une variable nommée raycastResult .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Créer un rayon à partir de la position de la souris 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Le vecteur de direction d'unité du rayon multiplié par une distance maximalelocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Lancer de rayons depuis l'origine du rayon vers sa directionlocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Informations de collision
Si l'opération de lancer de rayon trouve un objet frappé par le rayon, il retournera un RaycastResult , qui contient des informations sur la collision entre le rayon et l'objet.
Propriété RaycastResult | Avertissement |
---|---|
Instance | La cellule BasePart ou Terrain dans laquelle le rayon a intersecté. |
Position | Là où l'intersection s'est produite ; généralement un point directement sur la surface d'une partie ou d'un terrain. |
Matériel | Le matériau au point de collision. |
Ordinaire | Le vecteur normal de la face intersectionnée. Il peut être utilisé pour déterminer dans quelle direction la face pointe. |
La propriété Position sera la position de l'objet sur lequel la souris se déplace.Si la souris ne survole aucun objet à une distance de MAX_MOUSE_DISTANCE, raycastResult sera nil .
Créez une déclaration if pour vérifier si raycastResult existe.
Si raycastResult a une valeur, retournez sa propriété Position .
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 position de la souris 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Le vecteur de direction d'unité du rayon multiplié par une distance maximale
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Lancer de rayons depuis l'origine du rayon vers sa direction
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Retourner le point d'intersection 3D
return raycastResult.Position
else
-- Aucun objet n'a été touché, donc calculez la position à la fin du rayon
return screenToWorldRay.Origin + directionVector
end
end
Tirer vers la cible
Maintenant que la position de la souris 3D est connue, elle peut être utilisée comme position cible pour tirer un laser vers.Un deuxième rayon peut être lancé entre l'arme du joueur et la position cible en utilisant la fonction Raycast .
Déclarez une constante appelée MAX_LASER_DISTANCE au sommet du script et attribuez-la à 500 ou à la plage choisie pour le laser blaster.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Créez une fonction appelée fireWeapon sous la fonction getWorldMousePosition.
Appellez getWorldMousePosition et attribuez le résultat à une variable nommée position de la souris . Ce sera la position cible pour le lancer de rayons.
-- Aucun objet n'a été touché, donc calculez la position à la fin du rayonreturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Cette fois, le vecteur de direction pour la fonction de lancer de rayons représente la direction de la position de l'outil du joueur à l'emplacement cible.
Déclarez une variable nommée direction cible et calculez le vecteur de direction en soustrayant la position de l'outil de mouseLocation.
Normalisez le vecteur en utilisant sa propriété Unité . Cela lui donne une magnitude de 1, ce qui facilite le multipliage par une longueur plus tard.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Calculer un vecteur de direction normalisé et le multiplier par la distance laserlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitendDéclarez une variable nommée directionVector et attribuez-lui le targetDirection multiplié par le MAX_LASER_DISTANCE.
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- La direction pour tirer l'arme, multipliée par une distance maximalelocal directionVector = targetDirection * MAX_LASER_DISTANCEend
Un objet RaycastParams peut être utilisé pour stocker des paramètres supplémentaires pour la fonction de lancer de rayons.Il sera utilisé dans votre laser blaster pour s'assurer que le raycast ne se heurte pas accidentellement au joueur tirant l'arme.Toutes les parties incluses dans la propriété d'un objet RaycastParams seront ignorées dans le lancer de rayons.
Continuez la fonction fireWeapon et déclarez une variable appelée weaponRaycastParams . Attribuez un nouvel objet RaycastParams à elle.
Créer une table contenant le caractère local du joueur et l'attribuer à la propriété .
Lancer de rayons à partir de la position de la poignée de l'outil du joueur, dans une direction vers le directionVector.N'oubliez pas d'ajouter weaponRaycastParams comme argument cette fois.Attribuez 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()
-- Calculer un vecteur de direction normalisé et le multiplier par la distance 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 l'empêcher de se blesser
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 lancer de rayon a retourné une valeur.Si une valeur est renvoyée, un objet a été frappé par le rayon et un laser peut être créé entre l'arme et le lieu d'impact.Si rien n'a été renvoyé, la position finale doit être calculée afin de créer le laser.
Déclarez une variable vide nommée hitPosition .
Utilisez une déclaration si pour vérifier si weaponRaycastResult a une valeur. Si un objet a été frappé, attribuez weaponRaycastResult.Position à hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Vérifiez si des objets ont été frappés entre la position de départ et la position de finlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendSi weaponRaycastResult n'a pas de valeur, calculez la position finale du raycast en ajoutant ensemble la position **** de la poignée de l'outil avec le directionVector.Attribuez-le à hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Vérifiez si des objets ont été frappés entre la position de départ et la position de finlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Calculer la position finale en fonction de la distance laser maximalehitPosition = tool.Handle.Position + directionVectorendendAccé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érifier le coup d'objet
Pour trouver si l'objet touché par le laser fait partie du personnage d'un joueur ou est juste 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 de caractère caractère .Si une partie du personnage a été touchée, vous ne pouvez pas supposer que le parent de l'objet touché serait le personnage.Le laser aurait pu frapper une partie du corps, un accesoireou un outil, tous situés dans différentes parties de la hiérarchie du personnage.
Vous pouvez utiliser FindFirstAncestorOfClass pour trouver un ancêtre de modèle de caractère de l'objet frappé par le laser, s'il existe.Si vous trouvez un modèle et qu'il contient un humanoïde, dans la plupart des cas, vous pouvez supposer que c'est un personnage.
Ajoutez le code mis en évidence ci-dessous à la déclaration weaponRaycastResult si pour vérifier si un caractère a été frappé.
-- Vérifiez si des objets ont été frappés entre la position de départ et la position de finlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- La frappe d'instance sera un enfant d'un modèle de modèlisation-- Si un humanoïde est trouvé dans le modèle, il est probablement le personnage d'un joueurlocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Player hit")endendelse-- Calculer la position finale en fonction de la distance laser maximalehitPosition = tool.Handle.Position + directionVectorend
Maintenant, le laser blaster doit imprimer Player hit dans la fenêtre de sortie à chaque fois que l'opération de lancer de rayon frappe un autre joueur.
Tester avec plusieurs joueurs
Deux joueurs sont nécessaires pour tester si le rayon d'arme trouve d'autres joueurs, vous devez donc démarrer un serveur local.
Sélectionnez l'onglet test dans Studio.
Assurez-vous que la liste déroulante des joueurs est réglée sur « 2 joueurs » et cliquez sur le bouton Démarrer pour démarrer un serveur local avec 2 clients.Trois fenêtres apparaîtront.La première fenêtre sera le serveur local, les autres fenêtres seront les clients pour Player1 et Player2.
Sur un client, testez le tir de l'autre joueur avec l'arme en cliquant dessus.« Joueur touché » doit être affiché dans la sortie chaque fois qu'un joueur est touché.
Vous pouvez en savoir plus sur l'onglet test tab ici .
Trouver la position du laser
Le blaster devrait tirer un rayon de lumière rouge sur sa cible.La fonction pour cela sera à 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 devrait être rendu.
Créez un ModuleScript nommé LaserRenderer , hérité de StarterPlayerScripts sous StarterPlayer.
Ouvrez le script et renommez la table du module au nom du script LaserRenderer .
Déclarez une variable nommée DURÉE DE TIR avec une valeur de 0.15 .Ce sera le temps (en secondes) auquel le laser est visible.
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éer un faisceau laser à partir d'une position de départ vers une position finalefunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererDéclarez une variable nommée startPosition et définissez la propriété Position de toolHandle comme sa valeur.Ce sera la position du laser du joueur.
Déclarez une variable nommée laserDistance et soustrait endPosition de startPosition pour trouver la différence entre les deux vecteurs.Utilisez la propriété Magnitude de celle-ci pour obtenir la longueur du faisceau laser.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendDéclarez une variable laserCFrame pour stocker la position et l'orientation du faisceau laser.La position doit être le point moyen du début et de la fin du rayon.Utilisez CFrame.lookAt pour créer un nouveau CFrame situé à startPosition et orienté vers endPosition .Multipliez ceci par un nouveau CFrame avec une valeur de l'axe Z de moitié négative laserDistance pour obtenir le point médian.
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
Créer la partie laser
Maintenant que vous savez où créer un rayon laser, vous devez ajouter le rayon lui-même. Cela peut être fait facilement avec une partie Néon.
Déclarez une variable laserPart et attribuez-lui une nouvelle instance Part.
Définissez les propriétés suivantes de laserPart :
- Taille : Vector3.new(0.2, 0.2, distance laser)
- Cadre C : laserCFrame
- Ancré : vrai
- CanCollide : faux
- Couleur : Color3.fromRGB(225, 0, 0) (une couleur rouge forte)
- Matériel : Enum.Material.Neon
Parent laserPart Espace de travail .
Ajoutez la partie 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.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-- Ajouter un rayon laser au service débris à être 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 Contrôleur d'outil .
En haut du script Contrôleur d'outils , déclarez une variable nommée LaserRenderer et exigez le script du module LaserRenderer situé dans PlayerScripts.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentEn bas de la fonction fireWeapon, appelez la fonction LaserRenderer createLaser en utilisant la poignée d'outil et hitPosition en tant qu'arguments.
-- Calculer la position finale en fonction de la distance laser maximalehitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endTestez l'arme en cliquant sur le bouton Jouer. Un rayon laser devrait être visible entre l'arme et la souris lorsque l'outil est activé.
évaluerla cadence de tir de l'arme
Les armes ont besoin d'un délai entre chaque tir pour empêcher les joueurs de faire 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 d'un joueur.
Déclarez une variable en haut de la ToolController appelée FIRE_RATE .Ce sera le temps minimum entre chaque tir.Donnez-lui une valeur de votre choix ; cet exemple utilise 0,3 secondes.
Déclarez une autre variable en dessous appelée temps du dernier tir avec une valeur de 0 .Cela stocke la dernière fois que le joueur a tiré et sera mis à jour à chaque tir.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Créez une fonction appelée canShootWeapon sans paramètre.Cette fonction examinera combien de temps s'est écoulé depuis le dernier tir et renverra vrai ou faux.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Vérifiez si suffisamment de temps s'est écoulé depuis le dernier tirlocal function canShootWeapon()endlocal function getWorldMousePosition()À l'intérieur de la fonction, déclarez une variable nommée currentTime ; attribuez-lui le résultat de l'appel de la fonction tick().Cela renvoie le temps écoulé, en secondes, depuis le 1er janvier 1970 (une date arbitraire largement utilisée pour calculer le temps).
Soustraire le timeOfPreviousShot de currentTime et retourner faux si le résultat est inférieur à FIRE_RATE ; sinon, retourner vrai .
-- Vérifiez si suffisamment de temps s'est écoulé depuis le dernier tirlocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendÀ la fin de la fonction fireWeapon, mettez à jour timeOfPreviousShot chaque fois que l'arme est tirée en utilisant tick.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endÀ l'intérieur de la fonction toolActivated, créez une déclaration si et appelez canShootWeapon pour vérifier si l'arme peut être tirée.
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
Lorsque vous testez le blaster, vous devez trouver que, peu importe la vitesse à laquelle vous cliquer, il y aura toujours un court délai de 0,3 secondes entre chaque tir.
Dégâts le joueur
Les clients ne peuvent pas endommager d'autres clients directement ; le serveur doit être responsable de l'émission de dommages lorsqu'un joueur est touché.
Les clients peuvent utiliser un RemoteEvent pour dire au serveur qu'un caractère a été frappé.Ces derniers doivent être stockés dans ReplicatedStorage , où ils sont visibles à la fois pour le client et le serveur.
Créez un dossier dans ReplicatedStorage nommé événements .
Insérez un événement distant dans le dossier d'événements et nommez-le DamageCharacter .
Dans ToolController , créez des variables au début du script pour ReplicatedStorage et le dossier Événements.
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 = 500Remplacez la déclaration d'impression par une ligne de Luau pour déclencher l'événement à distance DamageCharacter avec la variable comme argument.
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel)endendelse-- Calculer la position finale en fonction de la distance laser maximalehitPosition = tool.Handle.Position + directionVectorend
Le serveur doit infliger des dommages au joueur qui a été touché lorsque l'événement est déclenché.
Insérez un script dans ServerScriptService et nommez-le ServerLaserManager .
Déclarez une variable nommée LASER_DAMAGE et définissez-la sur 10 ou une valeur de votre choix.
Créez une fonction nommée damageCharacter avec deux paramètres appelés playerFired et characterToDamage .
À l'intérieur de la fonction, trouvez le Humanoïde du personnage et soustrayez LASER_DAMAGE de sa santé.
Connectez la fonction damageCharacter à l'événement distant DamageCharacter dans le dossier d'événements.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10function damageCharacter(playerFired, characterToDamage)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Retirer la santé du personnagehumanoid.Health -= LASER_DAMAGEendend-- Relier les événements à des fonctions appropriéeseventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)Testez le blaster avec 2 joueurs en démarrant un serveur local.Lorsque vous tirez sur l'autre joueur, sa santé diminuera du nombre attribué à LASER_DAMAGE.
Afficher les faisceaux laser d'un autre joueur
Actuellement, le faisceau laser est créé par le client qui tire l'arme, donc seuls ils seront en mesure de voir le faisceau laser.
Si le faisceau laser a été créé sur le serveur, tout le monde pourrait le voir.Cependant, il y aurait un petit retard entre le client qui tire l'arme et le serveur qui reçoit les informations sur le tir.Cela signifierait que le client tirant l'arme verrait un délai entre le moment où il active l'arme et le moment où il voit le faisceau laser ; l'arme se sentirait lente en resultats.
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.D'autres clients subiront un léger retard entre le moment où un autre joueur tire et l'apparition d'un rayon.C'est le meilleur scénario : il n'y a aucun moyen de communiquer le laser d'un client à d'autres clients plus rapidement.
Client du tireur
Tout d'abord, le client doit indiquer au serveur qu'il a tiré un laser et fournir la position finale.
Insérez un événement distant dans le dossier événements dans ReplicatedStorage et nommez-le LaserFired .
Localisez la fonction fireWeapon dans le script Contrôleur d'outils .À la fin de la fonction, lancez l'événement distant LaserFired en utilisant hitPosition comme argument.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
Le serveur
Le serveur doit maintenant recevoir l'événement que le client a déclenché et informer tous les clients de la position de départ et de fin du faisceau laser afin qu'ils puissent également le rendre.
Dans le script ServerLaserManager , créez une fonction nommée playerFiredLaser au-dessus de damageCharacter avec deux paramètres appelés playerFired et endPosition.
Connectez la fonction à l'événement distant LaserFired .
-- Avertir tous les clients qu'un laser a été tiré afin qu'ils puissent afficher le laserlocal function playerFiredLaser(playerFired, endPosition)end-- Relier les événements à des fonctions appropriéeseventsFolder.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 là où cela est possible.La position de la poignée de l'arme du personnage sera la position de départ, afin que le serveur puisse la trouver là-bas.
Créez une fonction getPlayerToolHandle au-dessus de la fonction playerFiredLaser avec un paramètre appelé player .
Utilisez le code suivant pour rechercher le personnage du joueur pour l'arme et retourner l'objet poignée.
local LASER_DAMAGE = 10-- Trouvez la poignée de l'outil que le joueur tientlocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Avertir tous les clients qu'un laser a été tiré afin qu'ils puissent afficher le laserlocal function playerFiredLaser(playerFired, endPosition)
Le serveur peut désormais appeler FireAllClients sur l'événement distant LaserFired pour envoyer les informations requises pour rendre le laser aux clients.Cela inclut le joueur qui a tiré le laser (donc le client pour ce joueur ne rend pas le laser deux fois), la poignée du blaster (qui agit comme une position de départ pour le laser) et la position finale du laser.
Dans la fonction playerFiredLaser, appelez la fonction getPlayerToolHandle avec playerFired comme argument et attribuez la valeur à une variable nommée toolHandle .
Si toolHandle existe, lancez l'événement LaserFired pour tous les clients utilisant playerFired, toolHandle et endPosition comme arguments.
-- Avertir tous les clients qu'un laser a été tiré afin qu'ils puissent afficher le laserlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
Rendre sur les clients
Maintenant, FireAllClients a été appelé, chaque client recevra un événement du serveur pour rendre un rayon laser.Chaque client peut réutiliser le module LaserRenderer plus tôt pour rendre le faisceau laser en utilisant la position de la poignée et la valeur de position finale du outil envoyée par le serveur.Le joueur qui a tiré le rayon laser en premier lieu devrait ignorer cet événement sinon il verra 2 lasers.
Créez un LocalScript dans StarterPlayerScripts appelé ClientLaserManager .
À l'intérieur du script, exigez le module LaserRenderer .
Créez une fonction nommée createPlayerLaser avec les paramètres playerWhoShot , toolHandle et endPosition .
Connectez la fonction à l'événement distant LaserFired dans le dossier d'événements.
Dans la fonction, utilisez une déclaration si pour vérifier si ne correspond pas à LocalPlayer.
À l'intérieur de la déclaration if, appelez la fonction createLaser 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 joueurlocal function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)Testez le blaster avec 2 joueurs en démarrant un serveur local.Placez chaque client sur différents côtés de votre moniteur afin que vous puissiez voir les deux fenêtres en même temps.Lorsque vous tirez sur un client, vous devez voir le laser sur l'autre client.
Effets sonores
L'effet sonore de tir actuel ne joue que sur le client qui tire le projectile.Vous devrez déplacer le code pour jouer le son afin que d'autres joueurs l'entendent également.
Dans le script ToolController , naviguez vers la fonction toolActivated et supprimez la ligne qui joue le son d'activation.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendEn 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 Activer .
Utilisez une déclaration si pour vérifier si shootingSound existe ; si c'est le cas, appelez sa fonction Jouer .
laserPart.Parent = workspace-- Ajouter un rayon laser au service débris à être supprimé et nettoyéDebris:AddItem(laserPart, SHOT_DURATION)-- Jouer le son de tir de l'armelocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Récupérer à distance sécurisée en utilisant la validation
Si le serveur ne vérifie pas les données des demandes entrantes, un pirate peut abuser des fonctions et des événements à distance et les utiliser pour envoyer des valeurs fausses au serveur.Il est important d'utiliser la validation côté serveur pour empêcher cela.
Sous sa forme actuelle, l'événement à distance DamageCharacter est très vulnérable à l'attaque.Les pirates pourraient utiliser cet événement pour endommager n'importe quel joueur qu'ils veulent dans le jeu sans les 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érifiez si la distance entre le joueur et la position touchée par le laser se trouve dans une certaine limite.
- Lancer de rayons entre l'arme qui a tiré le laser et la position d'impact pour vous assurer que le tir était possible et n'a pas traversé aucun mur.
Client
Le client doit envoyer au serveur la position frappée par le raycast afin qu'il puisse vérifier que la distance est réaliste.
Dans ToolController , naviguez jusqu'à la ligne où l'événement de caractère de dommage est déclenché dans la fonction fireWeapon.
Ajoutez hitPosition comme argument.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Serveur
Le client envoie maintenant un paramètre supplémentaire via l'événement distant DamageCharacter, de sorte que le gestionnaire de laser du serveur doit être ajusté pour l'accepter.
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-- Retirer la santé du personnagehumanoid.Health -= LASER_DAMAGEendendEn dessous de la fonction getPlayerToolHandle, créez une fonction nommée isHitValid avec trois paramètres : playerFired, characterToDamage et hitPosition.
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
La première vérification sera la distance entre la position d'impact et le personnage touché.
Déclarez une variable nommée MAX_HIT_PROXIMITY au sommet du script et lui attribuez une valeur de 10 .Ce sera la distance maximale autorisée entre le coup et le personnage.Une tolérance est nécessaire car le caractère peut avoir légèrement bougé depuis que le client a déclenché l'événement.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10Dans la fonction isHitValid, calculez la distance entre le caractère et la position d'impact.Si la distance est supérieure à MAX_HIT_PROXIMITY alors retournez faux .
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Valider la distance entre le caractère frappé et la position d'impactlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
La deuxième vérification impliquera un lancer de rayon entre l'arme tirée et la position d'impact.Si le raycast renvoie un objet qui n'est pas le personnage, vous pouvez supposer que le tir n'était pas valide car quelque chose bloquait le tir.
Copiez le code ci-dessous pour effectuer cette vérifier. Retournez vrai à la fin de la fonction : si elle atteint la terminer, toutes les vérifications ont été effectuées.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Valider la distance entre le caractère frappé et la position d'impactlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Vérifiez si vous tirez à travers les murslocal toolHandle = getPlayerToolHandle(playerFired)if toolHandle thenlocal rayLength = (hitPosition - toolHandle.Position).Magnitudelocal rayDirection = (hitPosition - toolHandle.Position).Unitlocal raycastParams = RaycastParams.new()raycastParams.FilterDescendantsInstances = {playerFired.Character}local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)-- Si une instance a été touchée qui n'était pas le personnage, ignorez le tirif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendDéclarez une variable dans la fonction damageCharacter appelée validShot .Attribuez-lui le résultat d'un appel à la fonction isHitValid avec trois arguments : playerFired , characterToDamage et hitPosition .
Dans la déclaration if ci-dessous, ajoutez un opérateur et pour vérifier si validShot est vrai .
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")local validShot = isHitValid(playerFired, characterToDamage, hitPosition)if humanoid and validShot then-- Retirer la santé du personnagehumanoid.Health -= LASER_DAMAGEendend
Maintenant, l'événement de caractère de dommage est plus sûr et empêchera la plupart des joueurs d'en abuser.Notez que certains joueurs malveillants trouveront souvent des moyens de contourner la validation ; garder les événements à distance sécurisés est un effort continu.
Votre laser blaster est maintenant terminé, avec un système de détection de coup de base utilisant le raycasting.Essayez le tutoriel Détection de l'entrée de l'utilisateur pour découvrir comment vous pouvez ajouter une action de rechargement à votre laser blaster, ou créer une carte de jeu amusante et essayer votre laser blaster avec d'autres joueurs !
Codes code
Contrôleur d'outil
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érifiez 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 position de la souris 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Le vecteur de direction d'unité du rayon multiplié par une distance maximale
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Lancer de rayons depuis l'origine du roi vers sa direction
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Retourner le point d'intersection 3D
return raycastResult.Position
else
-- Aucun objet n'a été touché, donc calculez la position à la fin du rayon
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Calculer un vecteur de direction normalisé et le multiplier par la distance 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 l'empêcher de se blesser
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é frappés entre la position de départ et la position de fin
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- La frappe d'instance sera un enfant d'un modèle de modèlisation
-- Si un humanoïde est trouvé dans le modèle, il est probablement le 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
-- Calculer la position finale en fonction de la distance laser maximale
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)
Rendu laser
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Temps pendant lequel le laser est visible
-- Créer un faisceau laser à partir d'une position de départ vers une position finale
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
-- Ajouter un rayon laser au service débris à être supprimé et nettoyé
Debris:AddItem(laserPart, SHOT_DURATION)
-- Jouer le son de tir de l'arme
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer
Gestionnaire laser serveur
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Trouvez 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 caractère frappé et la position d'impact
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Vérifiez si vous tirez à travers les 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
-- Avertir 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
-- Retirer la santé du personnage
humanoid.Health -= LASER_DAMAGE
end
end
-- Relier les événements à des fonctions appropriées
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Gestionnaire laser client
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)