Dans ce tutoriel, vous apprendrez à lancer un laser à partir du blaster dans outils de création de joueur et à détecter si oui ou non 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 se heurte à des objets ou au terrain sur son chemin, il renvoie des informations sur la collision telles que la position et l'objet avec lequel il s'est heurté.
Trouver la position 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 faisant un raycasting à partir de la position de la souris 2D du joueur sur l'écran directement vers la caméra dans le monde du jeu. Le laser va collider avec tout ce que le joueur vise avec la souris.
Ouvrez le script ToolController à l'intérieur du script BlasterTool à partir de CreatingPlayerTools. Si vous n'avez pas encore terminé ce tutoriel, vous pouvez télécharger le modèle Blaster et l'insérer dans StarterPack.
Au début du script, déclarez une constant nommée MAX_MOUSE_DISTANCE avec une valeur de 1000 .
Créer 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-- Connectez des événements aux 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. Assignez ceci à 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, le lieu de souris 2D est connu, ses propriétés X et Y peuvent être utilisées comme paramètres pour la fonction Camera:ViewportPointToRay(), qui crée un 1> Datatype.Ray1> à partir de l'écran dans le monde de jeu 3D.
Utilisez les propriétés X et Y de mouseLocation comme arguments pour la fonction 1> Class.Camera:ViewportPointToRay()|ViewportPointToRay() . Assignez ceci à une variable nommée4> screenToWorldRay 4> .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Créez 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 vécteur de direction détermine la portée du rayon. Le rayon doit être aussi long que le MAX_MOUSE_DISTANCE, soit le double de la longueur du vécteur de direction.
Déclarez une variable nommée directionVector et attribuez-lui la valeur de screenToWorldRay.Direction multipliée par MAX_MOUSE_DISTANCE.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Créez un rayon à partir de la position de la souris 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- La direction de l'unité du rayon multipliée par une distance maximalelocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEAppnez-vous à utiliser la fonction Raycast de l'espace de travail, en passant la propriété origine de screenToWorldRay comme premier argument et 2> numberDirection2> comme deuxième. Assignez ceci à une variable nommée 5>raycastResult5>.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Créez un rayon à partir de la position de la souris 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- La direction de l'unité du rayon multipliée par une distance maximalelocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Raycast de l'origine du rayon vers sa directionlocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Informations de collision
Si l'opération de raycast trouve un objet touché par le rayon, il renverra un RaycastResult , qui contient des informations sur la collision entre le rayon et l'objet.
Propriété RaycastResult | Description |
---|---|
Instance | La BasePart ou Terrain cellule que le rayon a interceptée. |
Positionnez | Où l'intersection s'est produite ; généralement un point directement sur la surface d'une partie ou de terrain. |
Matériel | Le matériau au point d'impact. |
Normal | Le vécteur normal du visage interisé. Cela peut être utilisé pour déterminer dans quelle direction le visage pointe. |
La propriété Position sera la position de l'objet sur lequel la souris se trouve. Si la souris n'est pas sur aucun objet dans un rayon de MAX_MOUSE_DISTANCE , raycastResult sera nul.
Créez une if statement pour vérifier si raycastResult existe.
Si raycastResult a une valeur, renvoie sa propriété Position .
Si raycastResult est nul, trouvez la fin du raycast. Calculer la position 3D de la souris en ajoutant screenToWorldRay.Origin et directionVector ensemble.
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Créez un rayon à partir de la position de la souris 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- La direction de l'unité du rayon multipliée par une distance maximale
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast de 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é, donc calculer la position à la fin du rayon
return screenToWorldRay.Origin + directionVector
end
end
Tir vers la cible
Maintenant que la position de la souris 3D est connue, il peut être utilisé comme une position de cible pour tirer un laser vers. Un deuxième rayon peut être cast entre l'arme du joueur et la position de la cible en utilisant la fonction Raycast .
Déclarez une constant nommée MAX_LASER_DISTANCE au début du script et attribuez-la à 500 , ou votre portée choisie pour le laser Blaster.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Créer une fonction appelée fireWeapon sous la fonction getWorldMousePosition.
Appuyez sur getWorldMousePosition et attribuez le résultat à une variable nommée mousePosition . Ce sera la position cible pour le raycast.
-- Aucun objet n'a été touché, donc calculer 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 vécteur de direction pour la fonction raycast représentera la direction à partir de la position de l'outil du joueur à l'emplacement cible.
Déclarez une variable nommée directionDirection et calquez le vécteur de direction en soustrayant la position de l'outil de mouseLocation.
Normalise le vécteur en utilisant sa propriété Unité . Cela lui donne une magnitude de 1, ce qui facilite son multiplication plus tard.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Calculer un vécteur 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 à laquelle 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 lasercast. Il sera utilisé dans votre laser blaster pour vous assurer que le lasercast ne se heurte pas accidentellement avec le joueur tirant l'arme. Toutes les parties incluses dans la propriété FilterDescendantsInstances
Continuez la fonction fireWeapon et déclarez une variable nommée weaponRaycastParams . Assignez un nouveau objet RaycastParams à elle.
Créer une table contenant le personnage local du joueur et l'assigner à la propriété weaponRaycastParams.FilterDescendantsInstances.
Raycast à partir de la position de poignée de l'outil du joueur, dans une direction vers le directionVector. N'oubliez pas d'ajouter weaponRaycastParams en tant qu'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()
-- Calculer un vécteur de direction normalisé et le multiplier par la distance laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direction à laquelle tirer l'arme est multipliée par une distance maximale
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignorez le personnage du joueur pour les 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 raycast a restitué une valeur. Si une valeur est restituée, un objet a été touché par le laser et un objet peut être créé entre l'arme et le lieu d'impact. Si rien n'a été restitué, la position finale doit être calculée pour 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é touché, attribuez weaponRaycastResult.Position à 1> hitPosition1>.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Vérifiez si des objets ont été frappés entre la position de début et la finlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendSi weaponRaycastResult n'a pas de valeur, calculez la position de fin du lancer du rayon en ajoutant ensemble la position de la poignée de l'outil avec le DirectionVector . Assignez ceci à directionVector.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Vérifiez si des objets ont été frappés entre la position de début et la finlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Calculer la position de fin 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érification de l'impact de l'objet
Pour trouver si l'objet touché par le laser fait partie du personnage d'un joueur ou juste d'un paysage, vous devrez rechercher un Humanoid, car chaque personnage en a un.
Tout d'abord, vous devrez trouver le modèle de 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 a peut-être frappé une partie du corps, un accesoireou un outil, tous lesquels sont 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 touché par le laser, si un tel existe. Si vous trouvez un modèle et qu'il contient un humanoid, dans la plupart des cas, vous pouvez supposer qu'il s'agit d'un personnage.
Ajoutez le code souligné ci-dessous à la weaponRaycastResult si la déclaration a été renvoyée pour vérifier si un personnage a été touché.
-- Vérifiez si des objets ont été frappés entre la position de début et la finlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- L'instance touchée sera un enfant d'un modèle de modèlisation-- Si un humanoid est trouvé dans le modèle, il est probable qu'il s'agisse du 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 de fin en fonction de la distance laser maximalehitPosition = tool.Handle.Position + directionVectorend
Maintenant, le laser blaster devrait imprimer Player hit à la fenêtre de sortie chaque fois que l'opération de raycast frappe un autre joueur.
Tester avec plusieurs joueurs
Deux joueurs sont nécessaires pour tester si le laser d'arme trouve d'autres joueurs, vous devez donc démarrer un serveur local.
Sélectionnez l'onglet Test dans Studio.
Assurez-vous que le menu déroulant des joueurs est réglé 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. « Player touché » doit être affiché dans la sortie à chaque fois qu'un joueur est tiré.
Vous pouvez en savoir plus sur l'onglet Test ici.
Trouver la position du laser
Le blaster doit tirer un rayon de lumière rouge sur son 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 laser sera rendu.
Créer un ModuleScript nommé LaserRender , parenté à StarterPlayerScripts sous StarterPlayer.
Ouvrez le script et renommez la table de module au nom du script LaserRender .
Déclarez une variable nommée DURÉE DE TIR avec une valeur de 0.15 . Ce sera le temps (en secondes) que le laser est visible pour.
Créer une fonction de LaserRender nommée createLaser avec deux paramètres appelés toolHandle et endPosition .
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Temps que le laser est visible pour-- Créez un faisceau laser à partir d'une position de départ vers une position d'arrivéefunction 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 soustrayez endPosition de startPosition pour trouver la différence entre les deux vécteurs. Utilisez la propriété 1> Magnitude1> de cette variable 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 laser. La position doit être le point médian de la fin et du début du laser. Utilisez CFrame.lookAt pour créer une nouvelle CFrame à l
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 pièce laser
Maintenant que vous savez où créer un faisceau laser, vous devez ajouter le faisceau lui-même. Cela peut être facilement fait avec une partie Neon.
Déclarez une variable laserPart et attribuez-lui une nouvelle instance Part.
Définir les propriétés suivantes de laserPart :
- Taille : Vector3.new(0.2, 0.2, laserDistance)
- CFrame : laserCFrame
- Ancré : oui
- Peut-il s'entrer en collision : non
- 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-- Ajoutez un faisceau laser au service de débris pour être supprimé et nettoyéDebris:AddItem(laserPart, SHOT_DURATION)end
Maintenant, la fonction pour rendre le faisceau laser est terminée, il peut être appelé par le ToolController .
Au sommet du script ToolController , déclarez une variable nommée LaserRender et exigez le LaserRenderModuleScript situé dans PlayerScripts.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentAu bas de la fonction fireWeapon, appelez la fonction LaserRender createLaser en utilisant la poignée d'outil et hitPosition comme arguments.
-- Calculer la position de fin 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 faisceau laser devrait être visible entre l'arme et la souris lorsque l'outil est activé.
Contrôle de la cadence de tir de l'arme
Les armes ont un délai entre chaque tir pour empêcher les joueurs de porter trop de dégâts dans une courte période de temps. Cela peut être contrôlé en vérifiant si suffisamment de temps s'est écoulé depuis la dernière fois qu'un joueur a tiré.
Déclarez une variable au sommet du ToolController nommé FIRE_RATE . Ce sera le temps minimum entre chaque tir. Donnez-lui une valeur de votre choix ; cet exemple utilise 0,3 secondes .
Déclare une autre variable en dessous nommée timeOfPreviousShot avec une valeur de 0 . Cela stocke la dernière fois que le joueur a tiré et sera mis à jour avec chaque tir.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Créez une fonction appelée canShootWeapon sans argument. Cette fonction examinera combien de temps il s'est écoulé depuis le dernier tir et renverra vrai ou faux.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Vérifier si suffisamment de temps s'est écoulé depuis le dernier tirlocal function canShootWeapon()endlocal function getWorldMousePosition()Dans 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).
Soustrayez le timeOfPreviousShot de currentTime et renvoyez false si le résultat est inférieur à 1> FIRE_RATE1> ; sinon, renvoyez 4> true4> .
-- Vérifier 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)endDans 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 devriez trouver que, quelle que soit la vitesse à laquelle vous cliquer, il y a toujours un léger décalage de 0,3 secondes entre chaque tir.
Dégâts le joueur
Les clients ne peuvent pas endommager directement d'autres clients ; le serveur doit être responsable de l'émission de dégâts lorsqu'un joueur est touché.
Les clients peuvent utiliser un RemoteEvent pour informer le serveur qu'un personnage a été frappé. Ceux-ci doivent être stockés dans ReplicatedStorage , où ils sont visibles à la fois pour le client et le serveur.
Créer un dossier dans ReplicatedStorage nommé événements .
Insérez un événement distant dans le dossier événements et nommez-le DamageCharacter .
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.Parentlocal eventsFolder = ReplicatedStorage.Eventslocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Remplacez la "Player hit" dans fireWeapon par une ligne de Lua pour tirer l'événement distant DamageCharacter avec la variable 2>characterModel2> en tant qu'argument.
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel)endendelse-- Calculer la position de fin en fonction de la distance laser maximalehitPosition = tool.Handle.Position + directionVectorend
Le serveur doit infliger des dégâts 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éer une fonction nommée damageCharacter avec deux paramètres appelés playerFired et characterToDamage .
Dans la fonction, trouvez le Humanoid du personnage et soustrayez LASER_DAMAGE de sa santé.
Connectez la fonction damageCharacter à l'événement distant Dégâts personnage dans le dossier É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-- Supprimer la santé du personnagehumanoid.Health -= LASER_DAMAGEendend-- Connectez des événements aux fonctions appropriéeseventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)Testez le blaster avec 2 joueurs en commençant un serveur local. Lorsque vous tirez sur l'autre joueur, sa santé réduira le nombre attribué à LASER_DAMAGE .
Rendu des lasers d'un autre joueur
Actuellement, le laser est créé par le client tirant l'arme, donc seuls ils pourront voir le laser.
Si le laser a été créé sur le serveur, alors tout le monde pourrait le voir. Cependant, il y aurait un petit délai entre le client qui tire l'arme et le serveur qui reçoit l'information sur le tir. Cela signifierait que le client qui tire l'arme verrait un délai entre lorsqu'il active l'arme et lorsqu'il voit le laser. Cela signifierait que l'arme se sentirait décalée en resultats.
Pour résoudre ce problème, chaque client créera son propre faisceau laser. Cela signifie que le client qui tire sur l'arme verra le faisceau laser instantanément. D'autres clients vont expérimenter un léger délai entre lorsqu'un autre joueur tire et un faisceau apparaît. Ce est le meilleur scénario de cas : il n'y a aucun moyen de communiquer le laser d'un client à un autre client 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.
Insérez un événement distant dans le dossier événements dans ReplicatedStorage et nommez-le LaserFired .
Localisez la fonction fireWeapon dans le script ToolController . À la fin de la fonction, tirez l'événement distant LaserFired à l'aide de 1> hitPosition1> en tant qu'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émarrage et de fin du laser afin qu'ils puissent également le rendre.
Dans le script ServerLaserManager , créez une fonction nommée playerFiredLaser ci-dessus damageCharacter avec deux paramètres appelés 1> playerFired1> et 4> endPosition4>.
Connectez la fonction au LaserFired événement distant.
-- Informez tous les clients qu'un laser a été tiré pour qu'ils puissent afficher le laserlocal function playerFiredLaser(playerFired, endPosition)end-- Connectez des événements aux 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é du client, mais il est préférable d'éviter de faire confiance au client où possible. La position de poignée de l'arme du personnage sera la position de départ, afin que le serveur puisse le trouver à partir de là.
Créer une fonction getPlayerToolHandle au-dessus de la fonction playerFiredLaser avec un paramètre nommé player.
Utilisez le code suivant pour rechercher le personnage du joueur pour l'arme et retourner l'objet de poignée.
local LASER_DAMAGE = 10-- Trouvez la poignée de l'outil que le joueur détientlocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Informez tous les clients qu'un laser a été tiré pour qu'ils puissent afficher le laserlocal function playerFiredLaser(playerFired, endPosition)
Le serveur peut maintenant 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 (de sorte que le client pour ce joueur ne rend pas le laser deux fois), la poignée 2>de la main2> du blaster (qui agit comme une position de départ pour
Dans la fonction playerFiredLaser, appelez la fonction getPlayerToolHandle avec playerFired comme argument et attribuez la valeur à une variable nommée 1> toolHandle1> .
Si toolHandle existe, lancez l'événement LaserFired pour tous les clients en utilisant playerFired, toolHandle et 1> endPosition1> comme arguments.
-- Informez tous les clients qu'un laser a été tiré pour qu'ils puissent afficher le laserlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
Rendu sur les clients
Maintenant, 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 du serveur pour rendre le faisceau laser en utilisant la position et la valeur de pointe de l'outil envoyée par le serveur. Le joueur qui a tiré le faisceau laser en premier lieu devrait ignorer cet événement sinon ils verront 2 lasers.
Créer un LocalScript dans StarterPlayerScripts appelé ClientLaserManager .
Dans le script, requisez le module LaserRender .
Créer une fonction nommée createPlayerLaser avec les paramètres playerWhoShot , toolHandle et 1> endPosition1> .
Connectez la fonction à l'événement distant LaserFired dans le dossier Événements.
Dans la fonction, utilisez une déclaration si pour vérifier si playerWhoShot ne fait pas égal le LocalPlayer.
Dans la déclaration if, appelez la fonction createLaser à partir du module LaserRender 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 commençant un serveur local. Placez chacun des clients sur différents côtés de votre moniteur pour que vous puissiez voir les deux fenêtres à la fois. Lorsque vous tirez sur un client, vous devriez voir le laser sur l'autre côté.
Effets sonores
L'effet sonore de tir ne joue actuellement que sur le client qui tire le projectile. Vous devrez déplacer le code pour lancer le son afin que d'autres joueurs puissent l'entendre.
Dans le script ToolController , naviguez à la fonction outilActivated et enlevez la ligne qui joue le son Activate.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendAu bas de la fonction createLaser dans LaserRender , déclarez une variable nommée shootingSound et utilisez la méthode 1> Class.Instance:FindFirstChild()|FindFirstChild()1> de 4> toolHandle4> pour vérifier le son 7> activer7>.
Utilisez une déclaration si pour vérifier si shootingSound existe ; si oui, appelez sa fonction jouer .
laserPart.Parent = workspace-- Ajoutez un faisceau laser au service de débris pour être supprimé et nettoyéDebris:AddItem(laserPart, SHOT_DURATION)-- Jouez le son de tir de l'armelocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Sécuriser les télécommandes à l'aide de la validation
Si le serveur ne vérifie pas les données des demandes entrantes, un pirate peut abuser de fonctions et d'événements à distance et les utiliser pour envoyer des valeurs fausses au serveur. Il est important d'utiliser la validation côté serveur pour les empêcher.
Dans sa forme actuelle, l'événement distant DamageCharacter est très vulnérable aux attaques. Les pirates pourraient utiliser cet événement pour endommager n'importe quel joueur qu'ils voulaient dans le jeu sans les tirer.
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 frappée par le laser est dans une certaine limite.
- Raycast entre l'arme qui a tiré le laser et la position d'impact pour s'assurer que le tir était possible et n'a pas traversé de murs.
Client
Le client doit envoyer le serveur la position touché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 distant de DamageCharacter est déclenché dans la fonction fireWeapon.
Ajoutez hitPosition en tant qu'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, afin que le ServerLaserManager soit ajusté pour l'accepter.
Dans le script ServerLaserManager , ajoutez un hitPosition paramètre à la fonction damageCharacter.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Supprimer la santé du personnagehumanoid.Health -= LASER_DAMAGEendendSous la fonction getPlayerToolHandle, créez une fonction nommée estHitValid avec trois paramètres : playerFired , 1> characterToDamage1> et 4> hitPosition4> .
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
La première vérification sera la distance entre la position d'impact et la position de caractère.
Déclarez une variable nommée MAX_HIT_PROXIMITY au sommet du script et attribuez-lui 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 personnage peut avoir bougé légèrement 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, calulez la distance entre le personnage et la position d'impact. Si la distance est supérieure à MAX_HIT_PROXIMITY, renvoyez false .
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Valider la distance entre le personnage touché et la position de l'impactlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
La deuxième vérification impliquera un raycast entre l'arme tirée et la position de l'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 exécuter ce vérifier. Retournez vrai à la fin de la fonction : si elle atteint la terminer, tous les tests ont passé.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Valider la distance entre le personnage touché et la position de l'impactlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Vérifier 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é frappé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 : 1> playerFired1> , 4> characterToDamage4> et 7> hitPosition7> .
Dans la déclaration ci-dessous, ajoutez un et opérateur 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-- Supprimer la santé du personnagehumanoid.Health -= LASER_DAMAGEendend
Maintenant, l'événement distant de dégâts est plus sûr et empêchera la plupart des joueurs d'abuser. Remarquez que certains joueurs malveillants trouveront souvent des moyens autour de la validation ; garder les événements à distance un effort continu.
Votre laser est maintenant terminé, avec un système de détection de base utilisé par le raycasting. Essayez le Dépistage de l'entrée de l'utilisateur tutoriel pour savoir comment vous pouvez ajouter une action de rechargement à votre laser, ou créer une carte de jeu amusante et essayez votre laser avec d'autres joueurs !
Code final
Contrôleur d'outils
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éez un rayon à partir de la position de la souris 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- La direction de l'unité du rayon multipliée par une distance maximale
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast de l'origine du roi 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é, donc calculer la position à la fin du rayon
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Calculer un vécteur de direction normalisé et le multiplier par la distance laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direction à laquelle tirer l'arme, multipliée par une distance maximale
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignorez le personnage du joueur pour les 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ébut et la fin
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- L'instance touchée sera un enfant d'un modèle de modèlisation
-- Si un humanoid est trouvé dans le modèle, il est probable qu'il s'agisse 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
-- Calculer la position de fin 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)
LaserRender
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Temps que le laser est visible pour
-- Créez un faisceau laser à partir d'une position de départ vers une position d'arrivée
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
-- Ajoutez un faisceau laser au service de débris pour être 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
Gestionnaire laser du 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 dé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 de l'impact
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Vérifier 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é frappé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é pour 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
-- Supprimer la santé du personnage
humanoid.Health -= LASER_DAMAGE
end
end
-- Connectez des é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)