In questo tutorial, imparerai come emettere un laser dal blaster in Crea strumenti per i giocatori e rilevare se colpisce o meno un giocatore.
Raycasting per trovare collisioni
Raycasting crea un raggio invisibile da una posizione di partenza verso una direzione data con una lunghezza definita. Se il raggio collide con oggetti o terreno nel suo percorso, restituirà informazioni sulla collisione come la posizione e l'oggetto con cui ha colliso.

Trova la posizione del mouse
Prima che un laser possa essere sparato, devi prima sapere dove il giocatore sta mirando. Questo può essere trovato eseguendo un raycasting dalla posizione 2D del mouse del giocatore sullo schermo direttamente in avanti dalla telecamera nel mondo 3D. Il raggio colliderà con qualsiasi cosa su cui il giocatore sta mirando con il mouse.
Apri lo script ToolController all'interno dello strumento Blaster da Crea strumenti per i giocatori. Se non hai ancora completato quel tutorial, puoi scaricare il modello Blaster e inserirlo in StarterPack.

In cima allo script, dichiara una costante chiamata MAX_MOUSE_DISTANCE con un valore di 1000.
Crea una funzione chiamata 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-- Collega eventi alle funzioni appropriatetool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Usa la funzione GetMouseLocation di UserInputService per ottenere la posizione 2D del mouse del giocatore sullo schermo. Assegna questo a una variabile chiamata mouseLocation.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
Ora che la posizione 2D del mouse è nota, le sue proprietà X e Y possono essere utilizzate come parametri per la funzione Camera:ViewportPointToRay(), che crea un Ray dallo schermo nel mondo 3D.
Usa le proprietà X e Y di mouseLocation come argomenti per la funzione ViewportPointToRay(). Assegna questo a una variabile chiamata screenToWorldRay.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Crea un raggio dalla posizione 2D del mouselocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
È tempo di usare la funzione Raycast per controllare se il raggio colpisce un oggetto. Questo richiede una posizione di partenza e un vettore direzionale: in questo esempio, utilizzerai le proprietà origin e direction di screenToWorldRay.
La lunghezza del vettore direzionale determina quanto lontano viaggerà il raggio. Il raggio deve essere lungo quanto MAX_MOUSE_DISTANCE, quindi dovrai moltiplicare il vettore direzionale per MAX_MOUSE_DISTANCE.
Dichiara una variabile chiamata directionVector e assegnale il valore di screenToWorldRay.Direction moltiplicato per MAX_MOUSE_DISTANCE.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Crea un raggio dalla posizione 2D del mouselocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Il vettore direzionale unitario del raggio moltiplicato per una distanza massimalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEChiama la funzione Raycast di workspace, passando la proprietà Origin di screenToWorldRay come primo argomento e directionVector come secondo. Assegna questo a una variabile chiamata raycastResult.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Crea un raggio dalla posizione 2D del mouselocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Il vettore direzionale unitario del raggio moltiplicato per una distanza massimalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Raycast dall'origine del raggio verso la sua direzionelocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Informazioni sulla collisione
Se l'operazione di raycast trova un oggetto colpito dal raggio, restituirà un RaycastResult, che contiene informazioni sulla collisione tra il raggio e l'oggetto.
| Proprietà RaycastResult | Descrizione |
|---|---|
| Instance | Il BasePart o la cella Terrain con cui il raggio ha intersecato. |
| Position | Dove è avvenuta l'intersezione; di solito un punto direttamente sulla superficie di un pezzo o terreno. |
| Material | Il materiale al punto di collisione. |
| Normal | Il vettore normale della faccia intersecata. Questo può essere usato per determinare in quale direzione la faccia sta puntando. |
La proprietà Position sarà la posizione dell'oggetto su cui il mouse sta passando. Se il mouse non sta passando sopra alcun oggetto entro una distanza di MAX_MOUSE_DISTANCE, raycastResult sarà nil.
Crea un'istruzione if per controllare se raycastResult esiste.
Se raycastResult ha un valore, restituisci la sua proprietà Position.
Se raycastResult è nil, trova la fine del raycast. Calcola la posizione 3D del mouse aggiungendo insieme screenToWorldRay.Origin e directionVector.
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Crea un raggio dalla posizione 2D del mouse
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Il vettore direzionale unitario del raggio moltiplicato per una distanza massima
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast dall'origine del raggio verso la sua direzione
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Restituisci il punto 3D di intersezione
return raycastResult.Position
else
-- Nessun oggetto è stato colpito, quindi calcola la posizione alla fine del raggio
return screenToWorldRay.Origin + directionVector
end
endSpara verso il bersaglio
Ora che la posizione 3D del mouse è nota, può essere utilizzata come posizione bersaglio per sparare un laser. Un secondo raggio può essere emesso tra la posizione dell'arma del giocatore e la posizione bersaglio utilizzando la funzione Raycast.
Dichiara una costante chiamata MAX_LASER_DISTANCE in cima allo script e assegnale il valore di 500, o la tua gamma scelta per il blaster laser.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Crea una funzione chiamata fireWeapon sotto la funzione getWorldMousePosition.
Chiama getWorldMousePosition e assegna il risultato a una variabile chiamata mousePosition. Questa sarà la posizione bersaglio per il raycast.
-- Nessun oggetto è stato colpito, quindi calcola la posizione alla fine del raggioreturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Questa volta, il vettore direzionale per la funzione raycast rappresenterà la direzione dalla posizione dello strumento del giocatore alla posizione bersaglio.
Dichiara una variabile chiamata targetDirection e calcola il vettore direzionale sottraendo la posizione dello strumento da mouseLocation.
Normalizza il vettore utilizzando la sua proprietà Unit. Questo gli darà una magnitudine di 1, il che rende facile moltiplicarlo per una lunghezza successivamente.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Calcola un vettore direzionale normalizzato e moltiplica per la distanza del laserlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitendDichiara una variabile chiamata directionVector e assegnale il valore di targetDirection moltiplicato per MAX_LASER_DISTANCE.
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- La direzione per sparare l'arma, moltiplicata per una distanza massimalocal directionVector = targetDirection * MAX_LASER_DISTANCEend
Un oggetto RaycastParams può essere utilizzato per memorizzare parametri aggiuntivi per la funzione raycast. Sarà utilizzato nel tuo blaster laser per assicurarsi che il raycast non collide accidentalmente con il giocatore che spara l'arma. Qualsiasi parte inclusa nella proprietà FilterDescendantsInstances di un oggetto RaycastParams sarà ignorata nel raycast.
Continua la funzione fireWeapon e dichiara una variabile chiamata weaponRaycastParams. Assegna un nuovo oggetto RaycastParams a essa.
Crea una tabella contenente il personaggio locale del giocatore e assegnala alla proprietà weaponRaycastParams.FilterDescendantsInstances.
Esegui un raycast dalla posizione della maniglia dello strumento del giocatore, in una direzione verso il directionVector. Ricorda di aggiungere weaponRaycastParams come argomento questa volta. Assegna questo a una variabile chiamata 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()
-- Calcola un vettore direzionale normalizzato e moltiplica per la distanza del laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direzione per sparare l'arma moltiplicata per una distanza massima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignora il personaggio del giocatore per prevenire danni a se stessi
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
endInfine, dovrai controllare che l'operazione di raycast abbia restituito un valore. Se viene restituito un valore, un oggetto è stato colpito dal raggio e può essere creato un laser tra l'arma e la posizione colpita. Se non è stato restituito nulla, la posizione finale deve essere calcolata per creare il laser.
Dichiara una variabile vuota chiamata hitPosition.
Usa un'istruzione if per controllare se weaponRaycastResult ha un valore. Se un oggetto è stato colpito, assegna weaponRaycastResult.Position a hitPosition.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Controlla se sono stati colpiti oggetti tra la posizione di partenza e quella finalelocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendSe weaponRaycastResult non ha valore, calcola la posizione finale del raycast aggiungendo insieme la posizione della maniglia dello strumento con il directionVector. Assegna questo a hitPosition.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Controlla se sono stati colpiti oggetti tra la posizione di partenza e quella finalelocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Calcola la posizione finale in base alla distanza massima del laserhitPosition = tool.Handle.Position + directionVectorendendNaviga alla funzione toolActivated e chiama la funzione fireWeapon in modo che il laser venga sparato ogni volta che lo strumento viene attivato.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
Controlla l'oggetto colpito
Per scoprire se l'oggetto colpito dal laser fa parte del personaggio di un giocatore o è solo un pezzo di scenografia, dovrai cercare un Humanoid, poiché ogni personaggio ne ha uno.
Prima, dovrai trovare il modello del personaggio. Se una parte del personaggio è stata colpita, non puoi presumere che il genitore dell'oggetto colpito sia il personaggio. Il laser potrebbe aver colpito una parte del corpo, un accessorio o uno strumento, tutti situati in parti diverse della gerarchia del personaggio.

Puoi usare FindFirstAncestorOfClass per trovare un antenato del modello del personaggio dell'oggetto colpito dal laser, se esiste. Se trovi un modello e contiene un umanoide, nella maggior parte dei casi puoi presumere che sia un personaggio.
Aggiungi il codice evidenziato qui sotto all'istruzione if di weaponRaycastResult per controllare se è stato colpito un personaggio.
-- Controlla se sono stati colpiti oggetti tra la posizione di partenza e quella finalelocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- L'istanza colpita sarà un figlio di un modello di personaggio-- Se viene trovato un umanoide nel modello, allora è probabile che sia il personaggio di un giocatorelocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Giocatore colpito")endendelse-- Calcola la posizione finale in base alla distanza massima del laserhitPosition = tool.Handle.Position + directionVectorend
Ora il blaster laser dovrebbe stampare "Giocatore colpito" nella finestra Output ogni volta che l'operazione di raycast colpisce un altro giocatore.
Test con più giocatori
Sono necessari due giocatori per testare se il raycast dell'arma trova altri giocatori, quindi devi avviare un server locale. Vedi simulazione multi-client per istruzioni.
Su un client, prova a sparare all'altro giocatore con l'arma facendo clic su di lui. "Giocatore colpito" dovrebbe essere visualizzato nell'output ogni volta che un giocatore viene colpito.
Trova la posizione del laser
Il blaster dovrebbe sparare un raggio di luce rossa verso il suo bersaglio. La funzione per questo sarà all'interno di un ModuleScript in modo che possa essere riutilizzata in altri script in seguito. Prima, lo script dovrà trovare la posizione in cui il raggio laser dovrebbe essere reso.
Crea un ModuleScript chiamato LaserRenderer, genitore a StarterPlayerScripts sotto StarterPlayer.

Apri lo script e rinomina la tabella del modulo con il nome dello script LaserRenderer.
Dichiara una variabile chiamata SHOT_DURATION con un valore di 0.15. Questo sarà il tempo (in secondi) in cui il laser è visibile.
Crea una funzione di LaserRenderer chiamata createLaser con due parametri chiamati toolHandle e endPosition.
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Tempo in cui il laser è visibile-- Crea un raggio laser da una posizione di partenza verso una posizione finalefunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererDichiara una variabile chiamata startPosition e imposta la proprietà Position di toolHandle come suo valore. Questa sarà la posizione del blaster laser del giocatore.
Dichiara una variabile chiamata laserDistance e sottrai endPosition da startPosition per trovare la differenza tra i due vettori. Usa la proprietà Magnitude di questo per ottenere la lunghezza del raggio laser.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendDichiara una variabile laserCFrame per memorizzare la posizione e l'orientamento del raggio laser. La posizione deve essere il punto medio dell'inizio e della fine del raggio. Usa CFrame.lookAt per creare un nuovo CFrame situato in startPosition e rivolto verso endPosition. Moltiplica questo per un nuovo CFrame con un valore dell'asse Z pari alla metà di -laserDistance per ottenere il punto medio.
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
Crea la parte del laser
Ora che sai dove creare un raggio laser, devi aggiungere il raggio stesso. Questo può essere fatto facilmente con una parte Neon.
Dichiara una variabile laserPart e assegnale una nuova istanza di Part.
Imposta le seguenti proprietà di laserPart:
- Size: Vector3.new(0.2, 0.2, laserDistance)
- CFrame: laserCFrame
- Anchored: true
- CanCollide: false
- Color: Color3.fromRGB(225, 0, 0) (un colore rosso intenso)
- Material: Enum.Material.Neon
Genitore laserPart a Workspace.
Aggiungi la parte al servizio Debris in modo che venga rimossa dopo il numero di secondi nella variabile 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-- Aggiungi il raggio laser al servizio Debris per essere rimosso e pulitoDebris:AddItem(laserPart, SHOT_DURATION)end
Ora la funzione per rendere il raggio laser è completa, può essere chiamata dal ToolController.
In cima allo script ToolController, dichiara una variabile chiamata LaserRenderer e richiedi il modulo LaserRenderer situato in PlayerScripts.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentIn fondo alla funzione fireWeapon, chiama la funzione createLaser di LaserRenderer utilizzando la maniglia dello strumento e hitPosition come argomenti.
-- Calcola la posizione finale in base alla distanza massima del laserhitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endTesta l'arma facendo clic sul pulsante Play. Un raggio laser dovrebbe essere visibile tra l'arma e il mouse quando lo strumento viene attivato.
Controlla il tasso di fuoco dell'arma
Le armi necessitano di un ritardo tra ogni colpo per impedire ai giocatori di infliggere troppi danni in un breve lasso di tempo. Questo può essere controllato verificando se è passato abbastanza tempo dall'ultimo colpo sparato da un giocatore.
Dichiara una variabile in cima al ToolController chiamata FIRE_RATE. Questo sarà il tempo minimo tra ogni colpo. Dagli un valore a tua scelta; questo esempio utilizza 0.3 secondi.
Dichiara un'altra variabile sotto chiamata timeOfPreviousShot con un valore di 0. Questo memorizza l'ultimo momento in cui il giocatore ha sparato e verrà aggiornato con ogni colpo.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Crea una funzione chiamata canShootWeapon senza parametri. Questa funzione controllerà quanto tempo è passato dall'ultimo colpo e restituirà true o false.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Controlla se è passato abbastanza tempo dall'ultimo colpolocal function canShootWeapon()endlocal function getWorldMousePosition()All'interno della funzione dichiara una variabile chiamata currentTime; assegnale il risultato della chiamata alla funzione tick(). Questo restituisce quanto tempo è trascorso, in secondi, dal 1° gennaio 1970 (una data arbitraria ampiamente utilizzata per calcolare il tempo).
Sottrai timeOfPreviousShot da currentTime e restituisci false se il risultato è minore di FIRE_RATE; altrimenti, restituisci true.
-- Controlla se è passato abbastanza tempo dall'ultimo colpolocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendAlla fine della funzione fireWeapon, aggiorna timeOfPreviousShot ogni volta che l'arma viene sparata utilizzando tick.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endAll'interno della funzione toolActivated, crea un'istruzione if e chiama canShootWeapon per controllare se l'arma può essere sparata.
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
Quando testi il blaster, dovresti scoprire che non importa quanto velocemente clicchi, ci sarà sempre un breve ritardo di 0.3 secondi tra ogni colpo.
Danno al giocatore
I client non possono danneggiare direttamente altri client; il server deve essere responsabile dell'emissione del danno quando un giocatore viene colpito.
I client possono utilizzare un RemoteEvent per informare il server che un personaggio è stato colpito. Questi dovrebbero essere memorizzati in ReplicatedStorage, dove sono visibili sia al client che al server.
Crea una Cartella in ReplicatedStorage chiamata Events.

Inserisci un RemoteEvent nella cartella Events e chiamalo DamageCharacter.

In ToolController, crea variabili all'inizio dello script per ReplicatedStorage e la cartella 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 = 500Sostituisci l'istruzione di stampa "Giocatore colpito" in fireWeapon con una riga di Luau per attivare l'evento remoto DamageCharacter con la variabile characterModel come argomento.
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endendelse-- Calcola la posizione finale in base alla distanza massima del laserhitPosition = tool.Handle.Position + directionVectorend
Il server deve infliggere danno al giocatore che è stato colpito quando l'evento viene attivato.
Inserisci uno Script in ServerScriptService e chiamalo ServerLaserManager.

Dichiara una variabile chiamata LASER_DAMAGE e impostala su 10, o un valore a tua scelta.
Crea una funzione chiamata damageCharacter con due parametri chiamati playerFired e characterToDamage.
All'interno della funzione, trova l'Umanoide del personaggio e sottrai LASER_DAMAGE dalla sua salute.
Collega la funzione damageCharacter all'evento remoto DamageCharacter nella cartella Events.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10function damageCharacter(playerFired, characterToDamage)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Rimuovi salute dal personaggiohumanoid.Health -= LASER_DAMAGEendend-- Collega eventi alle funzioni appropriateeventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)Testa il blaster con 2 giocatori avviando un server locale. Quando spari all'altro giocatore, la sua salute diminuirà del valore assegnato a LASER_DAMAGE.
Rendi visibili i raggi laser degli altri giocatori
Attualmente, il raggio laser è creato dal client che spara l'arma, quindi solo lui sarà in grado di vedere il raggio laser.
Se il raggio laser fosse creato sul server, allora tutti sarebbero in grado di vederlo. Tuttavia, ci sarebbe un piccolo ritardo tra il client che spara l'arma e il server che riceve le informazioni sul colpo. Questo significherebbe che il client che spara l'arma vedrebbe un ritardo tra quando attiva l'arma e quando vede il raggio laser; l'arma sembrerebbe laggosa di conseguenza.
Per risolvere questo problema, ogni client creerà i propri raggi laser. Questo significa che il client che spara l'arma vedrà il raggio laser immediatamente. Altri client sperimenteranno un piccolo ritardo tra quando un altro giocatore spara e appare un raggio. Questo è il miglior scenario: non c'è modo di comunicare il laser di un client ad altri client più velocemente.
Client del tiratore
Prima, il client deve informare il server che ha sparato un laser e fornire la posizione finale.
Inserisci un RemoteEvent nella cartella Events in ReplicatedStorage e chiamalo LaserFired.

Trova la funzione fireWeapon nello script ToolController. Alla fine della funzione, attiva l'evento remoto LaserFired utilizzando hitPosition come argomento.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
Il server
Il server ora deve ricevere l'evento che il client ha sparato e informare tutti i client della posizione di partenza e di fine del raggio laser in modo che possano anche renderizzarlo.
Nello script ServerLaserManager, crea una funzione chiamata playerFiredLaser sopra damageCharacter con due parametri chiamati playerFired e endPosition.
Collega la funzione all'evento remoto LaserFired.
-- Notifica tutti i client che un laser è stato sparato in modo che possano visualizzare il laserlocal function playerFiredLaser(playerFired, endPosition)end-- Collega eventi alle funzioni appropriateeventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Il server ha bisogno della posizione di partenza del laser. Questo potrebbe essere inviato dal client, ma è meglio evitare di fidarsi del client quando possibile. La posizione della maniglia dell'arma del personaggio sarà la posizione di partenza, quindi il server può trovarla da lì.
Crea una funzione getPlayerToolHandle sopra la funzione playerFiredLaser con un parametro chiamato player.
Usa il seguente codice per cercare il personaggio del giocatore per l'arma e restituire l'oggetto maniglia.
local LASER_DAMAGE = 10-- Trova la maniglia dello strumento che il giocatore sta tenendolocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Notifica tutti i client che un laser è stato sparato in modo che possano visualizzare il laserlocal function playerFiredLaser(playerFired, endPosition)
Il server può ora chiamare FireAllClients sull'evento remoto LaserFired per inviare le informazioni necessarie per rendere il laser ai client. Questo include il giocatore che ha sparato il laser (in modo che il client per quel giocatore non renda il laser due volte), la maniglia del blaster (che funge da posizione di partenza per il laser) e la posizione finale del laser.
Nella funzione playerFiredLaser, chiama la funzione getPlayerToolHandle con playerFired come argomento e assegna il valore a una variabile chiamata toolHandle.
Se toolHandle esiste, attiva l'evento LaserFired per tutti i client utilizzando playerFired, toolHandle e endPosition come argomenti.
-- Notifica tutti i client che un laser è stato sparato in modo che possano visualizzare il laserlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
Rendi visibile sui client
Ora che FireAllClients è stato chiamato, ogni client riceverà un evento dal server per rendere un raggio laser. Ogni client può riutilizzare il modulo LaserRenderer di prima per rendere il raggio laser utilizzando la posizione della maniglia dello strumento e il valore di posizione finale inviato dal server. Il giocatore che ha sparato il raggio laser inizialmente dovrebbe ignorare questo evento altrimenti vedrà 2 raggi.
Crea un LocalScript in StarterPlayerScripts chiamato ClientLaserManager.

All'interno dello script, richiedi il modulo LaserRenderer.
Crea una funzione chiamata createPlayerLaser con i parametri playerWhoShot, toolHandle e endPosition.
Collega la funzione all'evento remoto LaserFired nella cartella Events.
Nella funzione, usa un'istruzione if per controllare se playerWhoShot è diverso da Players.LocalPlayer.
All'interno dell'istruzione if, chiama la funzione createLaser dal modulo LaserRenderer utilizzando toolHandle e endPosition come argomenti.
local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))local eventsFolder = ReplicatedStorage.Events-- Visualizza il raggio laser di un altro giocatorelocal function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)Testa il blaster con 2 giocatori avviando un server locale. Posiziona ogni client su lati diversi del monitor in modo da poter vedere entrambe le finestre contemporaneamente. Quando spari su un client dovresti vedere il raggio sull'altro client.
Effetti sonori
L'effetto sonoro di sparo attualmente viene riprodotto solo sul client che spara il proiettile. Dovrai spostare il codice per riprodurre il suono in modo che anche gli altri giocatori lo sentano.
Nello script ToolController, naviga alla funzione toolActivated e rimuovi la riga che riproduce il suono di attivazione.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendIn fondo alla funzione createLaser in LaserRenderer, dichiara una variabile chiamata shootingSound e usa il metodo FindFirstChild() di toolHandle per controllare il suono di Attivazione.
Usa un'istruzione if per controllare se shootingSound esiste; se esiste, chiama la sua funzione Play().
laserPart.Parent = workspace-- Aggiungi il raggio laser al servizio Debris per essere rimosso e pulitoDebris:AddItem(laserPart, SHOT_DURATION)-- Riproduci il suono di sparo dell'armalocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Sicurezza dei remoti tramite validazione
Se il server non controlla i dati delle richieste in arrivo, un hacker può abusare delle funzioni e degli eventi remoti e usarli per inviare valori falsi al server. È importante utilizzare la validazione lato server per prevenire questo.
Nella sua forma attuale, l'evento remoto DamageCharacter è molto vulnerabile agli attacchi. Gli hacker potrebbero utilizzare questo evento per danneggiare qualsiasi giocatore vogliano senza colpirli.
La validazione è il processo di verifica che i valori inviati al server siano realistici. In questo caso, il server dovrà:
- Controllare se la distanza tra il giocatore e la posizione colpita dal laser è entro un certo limite.
- Eseguire un raycast tra l'arma che ha sparato il laser e la posizione colpita per assicurarsi che il colpo fosse possibile e non sia passato attraverso alcun muro.
Client
Il client deve inviare al server la posizione colpita dal raycast in modo che possa controllare che la distanza sia realistica.
In ToolController, naviga alla riga in cui viene attivato l'evento DamageCharacter nella funzione fireWeapon.
Aggiungi hitPosition come argomento.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Server
Il client sta ora inviando un parametro extra attraverso l'evento remoto DamageCharacter, quindi il ServerLaserManager deve essere regolato per accettarlo.
Nello script ServerLaserManager, aggiungi un parametro hitPosition alla funzione damageCharacter.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Rimuovi salute dal personaggiohumanoid.Health -= LASER_DAMAGEendendSotto la funzione getPlayerToolHandle, crea una funzione chiamata isHitValid con tre parametri: playerFired, characterToDamage e hitPosition.
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
Il primo controllo sarà la distanza tra la posizione colpita e il personaggio colpito.
Dichiara una variabile chiamata MAX_HIT_PROXIMITY in cima allo script e assegnale un valore di 10. Questa sarà la distanza massima consentita tra il colpito e il personaggio. È necessaria una tolleranza perché il personaggio potrebbe essersi spostato leggermente da quando il client ha sparato l'evento.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10Nella funzione isHitValid, calcola la distanza tra il personaggio e la posizione colpita. Se la distanza è maggiore di MAX_HIT_PROXIMITY, restituisci false.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Valida la distanza tra il personaggio colpito e la posizione colpitalocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
Il secondo controllo comporterà un raycast tra l'arma sparata e la posizione colpita. Se il raycast restituisce un oggetto che non è il personaggio, puoi presumere che il colpo non fosse valido poiché qualcosa stava bloccando il colpo.
Copia il codice qui sotto per eseguire questo controllo. Restituisci true alla fine della funzione: se raggiunge la fine, tutti i controlli sono stati superati.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Valida la distanza tra il personaggio colpito e la posizione colpitalocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Controlla se si spara attraverso i murilocal toolHandle = getPlayerToolHandle(playerFired)if toolHandle thenlocal rayLength = (hitPosition - toolHandle.Position).Magnitudelocal rayDirection = (hitPosition - toolHandle.Position).Unitlocal raycastParams = RaycastParams.new()raycastParams.FilterDescendantsInstances = {playerFired.Character}local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)-- Se un'istanza è stata colpita che non era il personaggio, ignora il colpoif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendDichiara una variabile nella funzione damageCharacter chiamata validShot. Assegna ad essa il risultato di una chiamata alla funzione isHitValid con tre argomenti: playerFired, characterToDamage e hitPosition.
Nella successiva istruzione if, aggiungi un operatore and per controllare se validShot è true.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")local validShot = isHitValid(playerFired, characterToDamage, hitPosition)if humanoid and validShot then-- Rimuovi salute dal personaggiohumanoid.Health -= LASER_DAMAGEendend
Ora l'evento remoto damageCharacter è più sicuro e impedirà la maggior parte dei giocatori di abusarne. Tieni presente che alcuni giocatori malintenzionati troveranno spesso modi per aggirare la validazione; mantenere gli eventi remoti sicuri è uno sforzo continuo.
Il tuo blaster laser è ora completo, con un sistema di rilevamento colpi di base utilizzando il raycasting. Prova il tutorial Rilevamento input utente per scoprire come puoi aggiungere un'azione di ricarica al tuo blaster laser, o crea una mappa divertente e prova il tuo blaster laser con altri giocatori!
Codice finale
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
-- Controlla se è passato abbastanza tempo dall'ultimo colpo
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()
-- Crea un raggio dalla posizione 2D del mouse
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Il vettore direzionale unitario del raggio moltiplicato per una distanza massima
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast dall'origine del raggio verso la sua direzione
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Restituisci il punto 3D di intersezione
return raycastResult.Position
else
-- Nessun oggetto è stato colpito, quindi calcola la posizione alla fine del raggio
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Calcola un vettore direzionale normalizzato e moltiplica per la distanza del laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direzione per sparare l'arma, moltiplicata per una distanza massima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignora il personaggio del giocatore per prevenire danni a se stessi
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Controlla se sono stati colpiti oggetti tra la posizione di partenza e quella finale
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- L'istanza colpita sarà un figlio di un modello di personaggio
-- Se viene trovato un umanoide nel modello, allora è probabile che sia il personaggio di un giocatore
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
-- Calcola la posizione finale in base alla distanza massima del 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 -- Tempo in cui il laser è visibile
-- Crea un raggio laser da una posizione di partenza verso una posizione 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
-- Aggiungi il raggio laser al servizio Debris per essere rimosso e pulito
Debris:AddItem(laserPart, SHOT_DURATION)
-- Riproduci il suono di sparo dell'arma
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRendererServerLaserManager
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Trova la maniglia dello strumento che il giocatore sta tenendo
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)
-- Valida la distanza tra il personaggio colpito e la posizione colpita
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Controlla se si spara attraverso i muri
local toolHandle = getPlayerToolHandle(playerFired)
if toolHandle then
local rayLength = (hitPosition - toolHandle.Position).Magnitude
local rayDirection = (hitPosition - toolHandle.Position).Unit
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {playerFired.Character}
local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)
-- Se un'istanza è stata colpita che non era il personaggio, ignora il colpo
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Notifica tutti i client che un laser è stato sparato in modo che possano visualizzare il 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
-- Rimuovi salute dal personaggio
humanoid.Health -= LASER_DAMAGE
end
end
-- Collega eventi alle funzioni appropriate
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
-- Visualizza il raggio laser di un altro giocatore
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)