Inizia
Tutorial intermedi

Rilevamento colpi con laser

*Questo contenuto è tradotto usando AI (Beta) e potrebbe contenere errori. Per visualizzare questa pagina in inglese, clicca qui.

In questo tutorial, imparerai a lanciare un laser dal blaster in Crea utensili per i giocatori e a rilevare se colpisce 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 cammino, restituirà informazioni sulla collisione come la posizione e l'oggetto con cui è colliso.

Raycast da A verso B collidendo con un muro

Trova la posizione del mouse

Prima che un laser possa essere sparato, devi prima sapere dove il giocatore sta mirando. Questo può essere trovato raycasting dalla posizione 2D del mouse del giocatore sullo schermo direttamente in avanti dalla fotocamera nel mondo 3D. Il raggio colpirà qualunque cosa il giocatore stia mirando con il mouse.

  1. Apri lo script ToolController all'interno dell'utensile Blaster da Crea utensili per i giocatori. Se non hai ancora completato quel tutorial, puoi scaricare il modello del Blaster e inserirlo in StarterPack.

  2. In cima allo script, dichiara una costante chiamata MAX_MOUSE_DISTANCE con un valore di 1000.

  3. Crea una funzione chiamata getWorldMousePosition.


    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end
    local function toolActivated()
    tool.Handle.Activate:Play()
    end
    -- Collega eventi alle funzioni appropriate
    tool.Equipped:Connect(toolEquipped)
    tool.Activated:Connect(toolActivated)
  4. 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.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    end

Ora che è nota la posizione 2D del mouse, 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.

  1. Utilizza 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 mouse
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    end

È ora di utilizzare la funzione Raycast per controllare se il raggio colpisce un oggetto. Questo richiede una posizione di partenza e un vettore direzione: in questo esempio, utilizzerai le proprietà origin e direction di screenToWorldRay.

La lunghezza del vettore direzione determina quanto lontano viaggia il raggio. Il raggio deve essere lungo quanto MAX_MOUSE_DISTANCE, quindi dovrai moltiplicare il vettore direzione per MAX_MOUSE_DISTANCE.

  1. 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 mouse
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Il vettore direzione unitario del raggio moltiplicato per una distanza massima
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
  2. Chiama 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 mouse
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Il vettore direzione 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)

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à RaycastResultDescrizione
InstanceIl BasePart o la cella Terrain con cui il raggio è intersecato.
PositionIl punto in cui è avvenuta l'intersezione; di solito un punto direttamente sulla superficie di un oggetto o terreno.
MaterialIl materiale nel punto di collisione.
NormalIl vettore normale della faccia intersecata. Questo può essere usato per determinare in quale direzione punta la faccia.

La proprietà Position sarà la posizione dell'oggetto su cui il mouse sta passando sopra. Se il mouse non passa sopra alcun oggetto entro una distanza di MAX_MOUSE_DISTANCE, raycastResult sarà nil.

  1. Crea un'istruzione if per controllare se raycastResult esiste.

  2. Se raycastResult ha un valore, restituisci la sua proprietà Position.

  3. Se raycastResult è nil, trova la fine del raycast. Calcola la posizione 3D del mouse sommando 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 direzione 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
-- Restituisce 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

Spara verso il bersaglio

Ora che la posizione 3D del mouse è nota, può essere utilizzata come posizione bersaglio per sparare un laser. Può essere lanciato un secondo raggio tra la posizione dell'arma del giocatore e la posizione bersaglio usando la funzione Raycast.

  1. Dichiara una costante chiamata MAX_LASER_DISTANCE in cima allo script e assegnale il valore di 500, oppure il tuo intervallo scelto per il blaster laser.


    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  2. Crea una funzione chiamata fireWeapon sotto la funzione getWorldMousePosition.

  3. 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 raggio
    return screenToWorldRay.Origin + directionVector
    end
    end
    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end

Questa volta, il vettore direzione per la funzione raycast rappresenterà la direzione dalla posizione dell'utensile del giocatore alla posizione bersaglio.

  1. Dichiara una variabile chiamata targetDirection e calcola il vettore direzione sottraendo la posizione dell'utensile da mouseLocation.

  2. Normalizza il vettore usando la sua proprietà Unit. Questo gli dà una magnitudine di 1, il che rende facile moltiplicarlo per una lunghezza successivamente.


    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    -- Calcola un vettore direzione normalizzato e moltiplicalo per la distanza del laser
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. Dichiara 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 massima
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

Un oggetto RaycastParams può essere utilizzato per memorizzare parametri aggiuntivi per la funzione raycast. Verrà utilizzato nel tuo blaster laser per assicurarsi che il raycast non collida accidentalmente con il giocatore che spara l'arma. Qualsiasi parte inclusa nella proprietà FilterDescendantsInstances di un oggetto RaycastParams sarà ignorata nel raycast.

  1. Continua la funzione fireWeapon e dichiara una variabile chiamata weaponRaycastParams. Assegna un nuovo oggetto RaycastParams a essa.

  2. Crea una tabella contenente il carattere locale del giocatore e assegnala alla proprietà weaponRaycastParams.FilterDescendantsInstances.

  3. Esegui un raycast dalla posizione del manico dell'utensile 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 direzione normalizzato e moltiplicalo 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 evitare che si danneggi da solo
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end

Infine, 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, occorre calcolare la posizione finale per creare il laser.

  1. Dichiara una variabile vuota chiamata hitPosition.

  2. 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 l'inizio e la fine della posizione
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. Se weaponRaycastResult non ha valore, calcola la posizione finale del raycast sommando la posizione del manico dell'utensile con il directionVector. Assegna questo a hitPosition.


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Controlla se sono stati colpiti oggetti tra l'inizio e la fine della posizione
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    else
    -- Calcola la posizione finale basata sulla massima distanza del laser
    hitPosition = tool.Handle.Position + directionVector
    end
    end
  4. Naviga alla funzione toolActivated e chiama la funzione fireWeapon in modo che il laser venga sparato ogni volta che lo strumento è 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 sarebbe il personaggio. Il laser potrebbe aver colpito una parte del corpo, un accessorio o uno strumento, tutti i quali si trovano in parti diverse della gerarchia del personaggio.

Puoi usare FindFirstAncestorOfClass per trovare un antenato del modello del personaggio dell'oggetto colpito dal laser, se uno esiste. Se trovi un modello e contiene un umanoide, nella maggior parte dei casi puoi presumere che sia un personaggio.

  1. Aggiungi il codice evidenziato qui sotto all'istruzione if di weaponRaycastResult per controllare se un personaggio è stato colpito.


    -- Controlla se sono stati colpiti oggetti tra l'inizio e la fine della posizione
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    -- L'istanza colpita sarà un figlio di un modello di personaggio
    -- Se è 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
    print("Giocatore colpito")
    end
    end
    else
    -- Calcola la posizione finale basata sulla massima distanza del laser
    hitPosition = tool.Handle.Position + directionVector
    end

Ora il blaster laser dovrebbe stampare "Giocatore colpito" nella finestra Output ogni volta che l'operazione di raycast colpisce un altro giocatore.

Prova con più giocatori

Due giocatori sono necessari per testare se il raycast di un'arma sta trovando altri giocatori, quindi devi avviare un server locale. Consulta simulazione multi‑client per istruzioni.

In un client, prova a colpire l'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 così può essere riutilizzata in altri script in seguito. Prima, lo script dovrà trovare la posizione in cui il raggio laser dovrebbe essere reso.

  1. Crea uno ModuleScript chiamato LaserRenderer, genitore di StarterPlayerScripts sotto StarterPlayer.

  2. Apri lo script e rinomina la tabella del modulo con il nome dello script LaserRenderer.

  3. Dichiara una variabile chiamata SHOT_DURATION con un valore di 0.15. Questo sarà il tempo (in secondi) in cui il laser è visibile.

  4. 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 finale
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. Dichiara una variabile chiamata startPosition e imposta la proprietà Position di toolHandle come suo valore. Questa sarà la posizione del blaster laser del giocatore.

  6. 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.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    end
  7. Dichiara 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 di metà della lunghezza negativa di laserDistance per ottenere il punto medio.


    function LaserRenderer.createLaser(toolHandle, endPosition)
    local startPosition = toolHandle.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)
    end

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.

  1. Dichiara una variabile laserPart e assegnale una nuova istanza di Part.

  2. Imposta le seguenti proprietà di laserPart:

    1. Size: Vector3.new(0.2, 0.2, laserDistance)
    2. CFrame: laserCFrame
    3. Anchored: true
    4. CanCollide: false
    5. Color: Color3.fromRGB(225, 0, 0) (un forte colore rosso)
    6. Material: Enum.Material.Neon
  3. Parent laserPart a Workspace.

  4. 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.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)
    local laserPart = Instance.new("Part")
    laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)
    laserPart.CFrame = laserCFrame
    laserPart.Anchored = true
    laserPart.CanCollide = false
    laserPart.Color = Color3.fromRGB(225, 0, 0)
    laserPart.Material = Enum.Material.Neon
    laserPart.Parent = workspace
    -- Aggiungi il raggio laser al servizio Debris per essere rimosso e ripulito
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

Ora la funzione per rendere il raggio laser è completa, può essere chiamata dal ToolController.

  1. 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.Parent
  2. In fondo alla funzione fireWeapon, chiama la funzione createLaser di LaserRenderer usando il manico dell'utensile e hitPosition come argomenti.


    -- Calcola la posizione finale basata sulla massima distanza del laser
    hitPosition = tool.Handle.Position + directionVector
    end
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  3. Prova l'arma facendo clic sul pulsante Play. Un raggio laser dovrebbe essere visibile tra l'arma e il mouse quando lo strumento è attivato.

Controlla la frequenza di sparo dell'arma

Le armi necessitano di un ritardo tra ogni colpo per impedire ai giocatori di infliggere troppi danni in un breve periodo di tempo. Questo può essere controllato verificando se è passato abbastanza tempo dall'ultimo colpo di un giocatore.

  1. 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.

  2. Dichiara un'altra variabile sotto chiamata timeOfPreviousShot con un valore di 0. Questo memorizza l'ultimo tempo in cui il giocatore ha sparato e verrà aggiornato con ogni colpo.


    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 300
    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
  3. Crea una funzione chiamata canShootWeapon senza parametri. Questa funzione controllerà quanto tempo è passato dall'ultimo colpo e restituirà vero o falso.


    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
    -- Controlla se è passato abbastanza tempo dall'ultimo colpo
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. All'interno della funzione dichiara una variabile chiamata currentTime; assegnale il risultato della chiamata alla funzione tick(). Questo restituisce quanto tempo è passato, in secondi, dall'1 gennaio 1970 (una data arbitraria ampiamente usata per calcolare il tempo).

  5. Sottrai timeOfPreviousShot da currentTime e restituisci false se il risultato è minore di FIRE_RATE; altrimenti, restituisci true.


    -- 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
  6. Alla fine della funzione fireWeapon, aggiorna timeOfPreviousShot ogni volta che l'arma viene sparata usando tick.


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. All'interno della funzione toolActivated, crea un'istruzione if e chiama canShootWeapon per controllare se l'arma può essere sparata.


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

Quando testate 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 di infliggere danni quando un giocatore viene colpito.

I client possono usare un RemoteEvent per dire al server che un personaggio è stato colpito. Questi dovrebbero essere memorizzati in ReplicatedStorage, dove sono visibili sia al client che al server.

  1. Crea una Cartella in ReplicatedStorage chiamata Events.

  2. Inserisci un RemoteEvent nella cartella Events e chiamalo DamageCharacter.

  3. Nel 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.Parent
    local eventsFolder = ReplicatedStorage.Events
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  4. Sostituisci l'istruzione di stampa "Giocatore colpito" in fireWeapon con una riga di Luau per sparare l'evento remoto DamageCharacter con la variabile characterModel come argomento.


    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 basata sulla massima distanza del laser
    hitPosition = tool.Handle.Position + directionVector
    end

Il server deve infliggere danni al giocatore che è stato colpito quando l'evento viene sparato.

  1. Inserisci uno Script in ServerScriptService e chiamalo ServerLaserManager.

  2. Dichiara una variabile chiamata LASER_DAMAGE e impostala su 10, o un valore a tua scelta.

  3. Crea una funzione chiamata damageCharacter con due parametri chiamati playerFired e characterToDamage.

  4. All'interno della funzione, trova l'umanoide del personaggio e sottrai LASER_DAMAGE dalla sua salute.

  5. Collega la funzione damageCharacter all'evento remoto DamageCharacter nella cartella Events.


    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    function damageCharacter(playerFired, characterToDamage)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- Rimuovi salute dal personaggio
    humanoid.Health -= LASER_DAMAGE
    end
    end
    -- Collega eventi alle funzioni appropriate
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. Prova il blaster con 2 giocatori avviando un server locale. Quando colpisci l'altro giocatore, la sua salute diminuirà del numero assegnato a LASER_DAMAGE.

Rendi visibili i raggi laser di altri giocatori

Attualmente, il raggio laser viene creato dal client che spara l'arma, quindi solo loro saranno 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 riguardo al colpo. Questo significherebbe che il client che spara l'arma vedrebbe un ritardo tra quando attivano l'arma e quando vedono il raggio laser; l'arma sembrerebbe quindi laggosa come risultato.

Per risolvere questo problema, ogni client creerà i propri raggi laser. Questo significa che il client che spara l'arma vedrà immediatamente il raggio laser. 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

Innanzitutto, il client deve dire al server che ha sparato un laser e fornire la posizione finale.

  1. Inserisci un RemoteEvent nella cartella Events in ReplicatedStorage e chiamalo LaserFired.

  2. Trova la funzione fireWeapon nel ToolController. Alla fine della funzione, spara l'evento remoto LaserFired usando hitPosition come argomento.


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = 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 dire a tutti i client la posizione di partenza e di fine del raggio laser in modo che possano renderizzarlo anche loro.

  1. Nello script ServerLaserManager, crea una funzione chiamata playerFiredLaser sopra damageCharacter con due parametri chiamati playerFired e endPosition.

  2. Collega la funzione all'evento remoto LaserFired.


    -- Notifica tutti i client che è stato sparato un laser in modo che possano visualizzare il laser
    local function playerFiredLaser(playerFired, endPosition)
    end

    -- Collega eventi alle funzioni appropriate
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
    eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

Il server ha bisogno della posizione iniziale del laser. Questo potrebbe essere inviato dal client, ma è meglio evitare di fidarsi del client dove possibile. La posizione del manico dell'arma del personaggio sarà la posizione iniziale, quindi il server può trovarla da lì.

  1. Crea una funzione getPlayerToolHandle sopra la funzione playerFiredLaser con un parametro chiamato player.

  2. Usa il seguente codice per cercare nel personaggio del giocatore l'arma e restituire l'oggetto manico.


    local LASER_DAMAGE = 10
    -- Trova il manico 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
    -- Notifica tutti i client che è stato sparato un laser in modo che possano visualizzare il laser
    local 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 di quel giocatore non renda il laser due volte), il manico del blaster (che funge da posizione di partenza per il laser) e la posizione finale del laser.

  1. Nella funzione playerFiredLaser, chiama la funzione getPlayerToolHandle con playerFired come argomento e assegna il valore a una variabile chiamata toolHandle.

  2. Se toolHandle esiste, spara l'evento LaserFired per tutti i client usando playerFired, toolHandle e endPosition come argomenti.


    -- Notifica tutti i client che è stato sparato un laser 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

Rendi visibile ai client

Ora FireAllClients è stato chiamato, ogni client riceverà un evento dal server per rendere visibile un raggio laser. Ogni client può riutilizzare il modulo LaserRenderer di prima per rendere il raggio laser usando la posizione del manico dello strumento e il valore di posizione finale inviato dal server. Il giocatore che ha sparato il raggio laser in origine dovrebbe ignorare questo evento altrimenti vedrà 2 laser.

  1. Crea un LocalScript in StarterPlayerScripts chiamato ClientLaserManager.

  2. All'interno dello script, richiedi il modulo LaserRenderer.

  3. Crea una funzione chiamata createPlayerLaser con i parametri playerWhoShot, toolHandle e endPosition.

  4. Collega la funzione all'evento remoto LaserFired nella cartella Events.

  5. Nella funzione, usa un'istruzione if per controllare se playerWhoShot non è uguale a LocalPlayer.

  6. All'interno dell'istruzione if, chiama la funzione createLaser dal modulo LaserRenderer usando 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 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)
  7. Prova 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 laser 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 possano sentire.

  1. Nel ToolController, vai alla funzione toolActivated e rimuovi la riga che riproduce il suono di attivazione.


    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. In fondo alla funzione createLaser in LaserRenderer, dichiara una variabile chiamata shootingSound e utilizza il metodo FindFirstChild() di toolHandle per controllare il suono Activate.

  3. Usa un'istruzione if per controllare se shootingSound esiste; se sì, chiama la sua funzione Play().


    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

Sicurezza dei remoti usando la 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 validazione lato server per prevenire questo.

Nella sua forma attuale, l'evento remoto DamageCharacter è molto vulnerabile agli attacchi. Gli hacker potrebbero usare questo evento per danneggiare qualsiasi giocatore vogliano senza sparargli.

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 se la distanza è realistica.

  1. Nel ToolController, vai alla riga in cui l'evento DamageCharacter viene sparato nella funzione fireWeapon.

  2. Aggiungi hitPosition come argomento.


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

Server

Il client ora sta inviando un parametro extra attraverso l'evento DamageCharacter, quindi il ServerLaserManager deve essere regolato per accettarlo.

  1. 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 personaggio
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. Sotto la funzione getPlayerToolHandle, crea una funzione chiamata isHitValid con tre parametri: playerFired, characterToDamage e hitPosition.


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

Il primo controllo sarà la distanza tra la posizione colpita e il personaggio colpito.

  1. Dichiara una variabile chiamata MAX_HIT_PROXIMITY in cima allo script e assegnale un valore di 10. Questo sarà la massima distanza consentita tra il colpo e il personaggio. È necessaria una tolleranza poiché il personaggio potrebbe essersi spostato leggermente dal momento in cui il client ha sparato l'evento.


    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    local MAX_HIT_PROXIMITY = 10
  2. Nella funzione isHitValid, calcola la distanza tra il personaggio e la posizione colpita. Se la distanza è maggiore di MAX_HIT_PROXIMITY, allora restituisci false.


    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
    end

Il secondo controllo coinvolgerà un raycast tra l'arma sparata e la posizione colpita. Se il raycast colpisce un oggetto che non è il personaggio, puoi presumere che il colpo non fosse valido poiché qualcosa ha bloccato il colpo.

  1. 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 colpita
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 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
  2. Dichiara 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.

  3. Nella dichiarazione if sottostante, 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 personaggio
    humanoid.Health -= LASER_DAMAGE
    end
    end

Ora l'evento remoto damageCharacter è più sicuro e impedirà alla maggior parte dei giocatori di abusarne. Nota 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 raycasting. Prova il tutorial Rilevamento dell'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 direzione 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
-- Restituisce 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 direzione normalizzato e moltiplicalo 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 evitare che si danneggi da solo
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 l'inizio e la fine della posizione
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- L'istanza colpita sarà un figlio di un modello di personaggio
-- Se è 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 basata sulla massima distanza 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 ripulito
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 LaserRenderer

ServerLaserManager


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Trova il manico 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 è stato sparato un laser 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 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)
© 2026 Roblox Corporation. Roblox, il logo Roblox e Powering Imagination sono tra i nostri marchi registrati e non registrati negli Stati Uniti. e altri paesi.