Rilevamento colpi con laser

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

In questo Tutoriale, imparerai a lanciare un laser dal blaster in Crea strumenti per il giocatore e a determinare se colpisce un Giocatoreo no.

Raycasting per trovare le collisioni

Raycasting crea un raggio invisibile da una posizione di partenza verso una direzione specificata con una lunghezza definita. Se il raggio si scontra con oggetti o terreno sul suo percorso, restituisce informazioni sulla collisione come la posizione e l'oggetto con cui si è scontrato.

Raycast da A hacia B in collisione con un muro

Trovare la posizione del mouse

Prima che un laser possa essere sparato, devi prima sapere dove il giocatore punta con il mouse 2D. Questo può essere trovato facendo raycasting dalla posizione del mouse 2D del Giocatoresulla schermata direttamente verso il Mondodel gioco. Il raggio si scontrerà con qualsiasi cosa il giocatore punta con il Topo, or mouse as computer mouse.

  1. Apri lo script ToolController all'interno dello strumento Blaster da Creazione degli strumenti del giocatore. Se non hai ancora completato quel tutorial, puoi scaricare il modello Blaster e inserirlo in StarterPack.

  2. Nella parte superiore dello 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
    -- Connetti eventi a funzioni appropriate
    tool.Equipped:Connect(toolEquipped)
    tool.Activated:Connect(toolActivated)
  4. Usa la funzione GetMouseLocation di UserInputService per ottenere la posizione del mouse 2D del Giocatoresullo 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 la posizione del mouse 2D è conosciuta, le sue proprietà X e Y possono essere utilizzate come parametri per la funzione Camera:ViewportPointToRay(), che crea un 1> Datatype.Ray1> dalla schermata nel Mondodel gioco 3D.

  1. Usa le proprietà X e Y di mouseLocation come argomenti per la funzione 1> Class.Camera:ViewportPointToRay()|ViewportPointToRay() . Assegna questo a una variabile chiamata4> screenToWorldRay4> .


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Crea un raggio dalla posizione del mouse 2D
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    end

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

La lunghezza del vettore di direzione determina la distanza percorsa dal raggio. Il raggio deve essere lungo come il MAX_MOUSE_DISTANCE , quindi dovrai moltiplicare il vettore di direzione per MAX_MOUSE_DISTANCE .

  1. Dichiarare una variabile chiamata direzioneVector e assegnarle il valore di screenToWorldRay.Direction moltiplicato da MAX_MOUSE_DISTANCE .


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Crea un raggio dalla posizione del mouse 2D
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- La direzione di unità del ray multiplicata da una distanza massima
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
  2. Chiama la funzione Raycast dello workspace, passando la proprietà Origin di screenToWorldRay come primo argomento e 1> durationVector1> come secondo. Assign this to a variable named 4> raycastResult4> .


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Crea un raggio dalla posizione del mouse 2D
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- La direzione di unità del ray multiplicata da 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 raycast trova un oggetto colpito dal ray, restituirà un RaycastResult , che contiene informazioni sulla collisione tra il ray e l'oggetto.

Proprietà RaycastResultDescrizione
istanzaLa BasePart o Terrain cellula che il raggio ha interseccato.
PosizioneDove si è verificata l'intersezione; di solito un punto direttamente sulla superficie di una parte o terreno.
MaterialeIl materiale al punto di collisione.
NormaleIl normale vector della faccia interrotta. Questo può essere utilizzato per determinare in quale direzione la faccia punta.

La proprietà Posizione sarà la posizione dell'oggetto che il mouse si trova sopra. Se il mouse non si trova su nessun oggetto all'interno di una distanza di MAX_MOUSE_DISTANCE , raycastResult sarà nullo.

  1. Crea un if statement per controllare se raycastResult esiste.

  2. Se raycastResult ha un valore, restituisci la sua Proprietà Posizione .

  3. Se raycastResult è nullo, allora trova la fine del raycast. Calcola la posizione 3D della mouse aggiungendo screenToWorldRay.Origin e directionVector insieme.


local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Crea un raggio dalla posizione del mouse 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- La direzione di unità del ray multiplicata da 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

Sparando verso il bersaglio

Ora che la posizione del mouse 3D è nota, può essere utilizzata come posizione di destinazione per sparare un laser verso. Un secondo raggio può essere cast tra l'arma del Giocatoree la posizione di destinazione usando la funzione Raggiocast .

  1. Dichiarare una costante chiamata MAX_LASER_DISTANCE nella parte superiore dello script e assegnarla a 500 , o la tua gamma scelta per il laser blaster.


    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 di destinazione 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 di direzione per la funzione raycast rappresenterà la direzione dalla posizione dell'utensile del Giocatorealla posizione di destinazione.

  1. Dichiarare una variabile chiamata targetDirection e calcolare il veicolo di direzione sottraendo la posizione dell'strumento da mouseLocation .

  2. Normalizza il veicolo utilizzando la sua Proprietà Unit . Ciò lo rende facile da moltiplicare in seguito con una lunghezza.


    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    -- Calcola un vettore di direzione normalizzato e moltiplica per la distanza laser
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. Dichiarare una variabile chiamata 向zione Vector e assegnare a essa il targetDirection moltiplicato per il MAX_LASER_DISTANCE .


    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    -- La direzione a cui sparare l'arma, moltiplicata da una distanza massima
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

Un oggetto RaycastParams può essere utilizzato per memorizzare parametri aggiuntivi per la funzione raycast. Sarà utilizzato nel tuo laser blaster per assicurarsi che il raycast non si sconvolga accidentalmente con il giocatore che spara l'arma. Tutte le parti incluse nella proprietà Datatype.RaycastParams.FilterDescendantsInstances|Filter

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

  2. Crea una tabella che contiene il personaggio locale del Giocatoree assegnalo alla proprietà weaponRaycastParams.FilterDescendantsInstances .

  3. Raycast dalla posizione della maniglia dell'strumento del Giocatore, in una direzione verso il directionVector . Ricorda di aggiungere weaponRaycastParams come argument this time. Assign this to a variable named 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 di direzione normalizzato e moltiplica per la distanza laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direzione per cui sparare l'arma si moltiplica per una distanza massima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignora il personaggio del Giocatoreper impedirgli di danneggiarsi
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 raycast abbia restituito un valore. Se viene restituito un valore, un oggetto viene colpito dal raggio e viene creato un laser tra l'arma e la posizione di colpo. Se non è stato restituito nulla, la posizione finale deve essere calcolata per creare il laser.

  1. Dichiarare una variabile vuota chiamata hitPosition .

  2. Usa una dichiarazione se per controllare se weaponRaycastResult ha un valore. Se un oggetto è stato colpito, assegnare weaponRaycastResult.Position a 1> hitPosition1> .


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Controlla se sono stati colpiti oggetti tra la posizione di partenza e la posizione di fine
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. Se weaponRaycastResult non ha valore, calcola la posizione della fine del raycast aggiungendo insieme la posizione della maniglia del strumento con il directionVector. Assegna questo a 1> hitPosition1> .


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Controlla se sono stati colpiti oggetti tra la posizione di partenza e la posizione di fine
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    else
    -- Calcola la posizione di fine raggio in base alla distanza laser massima
    hitPosition = tool.Handle.Position + directionVector
    end
    end
  4. Navigate to the toolActivated function and call the fireWeapon function so that the laser fires each time the tool is activated.


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

Controllo dell'oggetto colpito

Per trovare se l'oggetto colpito dal laser fa parte del personaggio di un Giocatoreo solo di un pezzo di sceneria, dovrai cercare un Humanoid, poiché ogni personaggio ne ha uno.

Per prima cosa, dovrai trovare il modello di personaggio . Se una parte del personaggio è stata colpita, non puoi assumere che il padre dell'oggetto colpito fosse il personaggio. Il laser potrebbe aver colpito una parte del corpo, un Accessorioo uno strumento, tutti i quali sono situati in diverse parti della gerarchia del personaggio.

Puoi usare FindFirstAncestorOfClass to find a character model ancestor of the object hit by the laser, if one exists. If you find a model and it contains a humanoid, in most cases you can assume it's a character.

  1. Aggiungi il codice evidenziato di seguito alla weaponRaycastResult if dichiarazione per controllare se un personaggio è stato colpito.


    -- Controlla se sono stati colpiti oggetti tra la posizione di partenza e la posizione di fine
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    -- L'istanza colpita sarà un figlio di un modello di personaggio
    -- Se viene trovato un humanoid nel modello, è 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("Player hit")
    end
    end
    else
    -- Calcola la posizione di fine raggio in base alla distanza laser massima
    hitPosition = tool.Handle.Position + directionVector
    end

Ora il laser blaster dovrebbe stampare Player hit alla finestra di uscita ogni volta che l'operazione raycast colpisce un altro Giocatore.

Testare con più giocatori

Sono necessari due giocatori per testare se il raggio dell'arma sta trovando altri giocatori, quindi devi iniziare un Serverlocale.

  1. Seleziona la scheda Test in Studio.

  2. Assicurati che il menu a discesa dei giocatori sia impostato su "2 giocatori" e fai clic sul pulsante Start per avviare un server locale con 2 client. Tre finestre appariranno. La prima finestra sarà il Serverlocale, le altre finire saranno i client per Player1 e Player2.

  3. Su un client, testare di sparare l'altro giocatore con l'arma facendo clic su di loro. "Player hit" dovrebbe essere visualizzato nell'output ogni volta che un giocatore viene colpito.

Puoi saperne di più sulla scheda Test qui .

Trovare la posizione del laser

Il blaster dovrebbe sparare un raggio di luce rosso al suo target. La funzione per questo sarà all'interno di un ModuleScript in modo che possa essere riutilizzato in altri script in seguito. Prima, lo script dovrà trovare la posizione in cui il raggio laser dovrebbe essere generato.

  1. Crea un ModuleScript chiamato LaserRender , che è figlio di StarterPlayerScripts sotto StarterPlayer.

  2. Apri lo script e rinomina la tabella del modulo in nome dello script LaserRender .

  3. Dichiarare una variabile chiamata SHOT_DURATION con un valore di 0.15 . Questa sarà la quantità di tempo (in secondi) per cui il laser è visibile.

  4. Crea una funzione di LaserRender chiamata createLaser con due parametri chiamati toolHandle e endPosition .


    local LaserRenderer = {}
    local SHOT_DURATION = 0.15 -- Tempo durante cui il laser è visibile
    -- Crea un raggio laser dalla posizione di partenza verso una posizione di destinazione
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. Dichiarare una variabile chiamata startPosition e impostare la proprietà Position di toolHandle come suo valore. Questa sarà la posizione del laser del Giocatore.

  6. Dichiarare una variabile chiamata laserDistance e sottrarre endPosition da startPosition per trovare la differenza tra i due veettori. Usa la proprietà 1> Magnitude1> 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. Dichiarare una variabile laserCFrame per memorizzare la posizione e l'orientamento del raggio laser. La posizione deve essere il punto medio della partenza e la fine del raggio. Usa CFrame.lookAt per creare una nuova Datatype.CFrame</


    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

Creazione della parte laser

Ora che sai dove creare un raggio laser, devi aggiungere il raggio stesso. Questo può essere fatto facilmente con una parte Neon.

  1. Dichiarare una variabile laserPart e assegnare a essa una nuova esempioPart .

  2. Imposta le seguenti proprietà di laserPart :

    1. Dimensione : Vector3.new(0.2, 0.2, laserDistance)
    2. CFrame : laserCFrame
    3. Anchored : vero
    4. CanCollide : false
    5. Colore : Color3.fromRGB(225, 0, 0) (un forte colore rosso)
    6. Materiale : 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 raggio laser al servizio Debris per essere rimosso e pulito
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

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

  1. Nella parte superiore dello script ToolController , dichiara una variabile chiamata LaserRenderer e richiedi il modulo di script LaserRendererModuleScript 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. Nella parte inferiore della funzione fireWeapon, chiama la funzione LaserRender createLaser utilizzando il trattino degli strumenti e hitPosition come argomenti.


    -- Calcola la posizione di fine raggio in base alla distanza laser massima
    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.

Controllo della velocità di fuoco dell'arma

Le armi hanno un ritardo tra ogni colpo per impedire ai giocatori di infliggere troppo danno in un breve periodo di tempo. Questo può essere controllato controllando se abbia passato abbastanza tempo dal giocatore l'ultima volta che ha sparato.

  1. Dichiarare una variabile nella parte superiore del ToolController chiamato FIRE_RATE . Questo sarà il tempo minimo tra ogni colpo. Dai un valore a scelta tua; questo esempio usa 0.3 secondi.

  2. Dichiarare un'altra variabile sotto chiamata timeOfPreviousShot con un valore di 0 . Questo memorizza l'ultimo tempo che il giocatore ha sparato e verrà aggiornato con ogni sparo.


    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 guarderà a quanto tempo è passato dal precedente colpo e restituirà vero o falso.


    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
    -- Controlla se sia passato abbastanza tempo dal momento in cui è stato sparato l'ultimo colpo
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. All'interno della funzione dichiara una variabile chiamata currentTime ; assegna al risultato dell'invocazione della funzione tick() . Questo restituisce il tempo trascorso, in secondi, dal primo gennaio 1970 (una data arbitraria ampiamente utilizzata per calcolare il tempo).

  5. Sottrai il timeOfPreviousShot da currentTime e restituisci false se il risultato è inferiore a 1> FIRE_RATE1>; altrimenti, restituisci 4> true 4> .


    -- Controlla se sia passato abbastanza tempo dal momento in cui è stato sparato l'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 se 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 testi il blaster dovresti trovare che, indipendentemente dalla velocità con cui fai clic, ci sarà sempre un breve ritardo di 0,3 secondi tra ogni colpo.

Danneggiare il giocatore

I client non possono danneggiare direttamente altri client; il server deve essere responsabile per l'emissione del danno quando un giocatore viene colpito.

I client possono utilizzare un RemoteEvent per informare il server che un personaggio è stato colpito. Questi devono essere memorizzati in ReplicatedStorage , dove sono visibili sia per il client che per il Server.

  1. Crea una cartella in ReplicatedStorage chiamata Eventi .

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

  3. In ToolController , crea variabili all'inizio dello script per ReplicatedStorage e la cartella Eventi.


    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 la print statement in fireWeapon con una linea di Lua per il fuoco del DamageCharacter evento remoto con la variabile 2> characterModel2> come argomento.


    local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    eventsFolder.DamageCharacter:FireServer(characterModel)
    end
    end
    else
    -- Calcola la posizione di fine raggio in base alla distanza laser massima
    hitPosition = tool.Handle.Position + directionVector
    end

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

  1. Inserisci uno Script in ServerScriptService e chiamalo ServerLaserManager.

  2. Dichiarare una variabile chiamata LASER_DAMAGE e impostarla 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 il carattere del suo Umanoide e sottrai LASER_DAMAGE dalla sua salute.

  5. Connetti la funzione damageCharacter alla remota evento DamageCharacter nella cartella Eventi.


    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 la salute dal personaggio
    humanoid.Health -= LASER_DAMAGE
    end
    end
    -- Connetti eventi a funzioni appropriate
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. Prova il blaster con 2 giocatori iniziando un Serverlocale. Quando spari l'altro Giocatore, la loro salute diminuirà del numero assegnato a LASER_DAMAGE .

Rendere i laser del giocatore

Attualmente, il raggio laser viene creato dal client che spara l'arma, quindi solo loro potranno vedere il raggio laser.

Se il laser beam è stato 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. Ciò significerebbe che il client che spara l'arma vedrebbe un ritardo tra quando attiva l'arma e quando vede il laser beam; l'arma si sentirebbe laggy come Risultato.

Per risolvere questo problema, ogni client creerà i suoi laser. Ciò significa che il cliente che spara l'arma vedrà il laser istantaneamente. Altri cliente vedranno un breve ritardo tra quando un altro giocatore spara e un raggio appare. Questo è lo scenario di caso migliore: non ci sono modo di comunicare un cliente del laser a altri clienti più velocemente.

Client del fotografo

Innanzitutto, il client deve informare il server di aver sparato un laser e fornire la posizione di fine.

  1. Inserisci un RemoteEvent nel cartello degli eventi in ReplicatedStorage e chiamalo LaserFired .

  2. Individua la funzione fireWeapon nella script ToolController . Alla fine della funzione, attiva l'evento remoto LaserFired utilizzando 1> hitPosition1> 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 avviato e comunicare a tutti i client la posizione di avvio e fine del laser in modo che possano anche renderlo.

  1. Nell'script del ServerLaserManager, crea una funzione chiamata playerFiredLaser sopra damageCharacter con due parametri chiamati 2> playerFired2> e 5> endPosition5> .

  2. Connetti la funzione all'evento remoto LaserFired .


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

    -- Connetti eventi a funzioni appropriate
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
    eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

Il server ha bisogno della posizione di avvio del laser. Questo potrebbe essere inviato dal client, ma è meglio evitare di fidarsi del client dove possibile. La posizione della maniglia dell'arma del personaggio sarà la posizione di avvio, in modo che il server possa trovarla da lì.

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

  2. Usa il seguente codice per cercare il personaggio del Giocatoreper l'arma e restituire l'oggetto maniglia.


    local LASER_DAMAGE = 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
    -- Avvisa tutti i client che un laser è stato sparato in modo che possano visualizzare il laser
    local function playerFiredLaser(playerFired, endPosition)

Il server ora può chiamare FireAllClients sull'evento remoto LaserFired per inviare le informazioni richieste per rendere il laser ai client. Ciò include il giocatore che ha sparato il laser (in modo che il client per quel giocatore non renda il laser due volte) e la posizione di fine del laser.

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

  2. Se toolHandle esiste, fire the LaserFired event for all client using playerFired , toolHandle and 1> endPosition1> as arguments.


    -- Avvisa 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

Renderizzazione sui client

Ora FireAllClients è stato chiamato, ogni cliente riceverà un evento dal server per rendere un raggio laser. Ogni cliente può riutilizzare il modulo LaserReceiver da precedente per rendere il raggio laser utilizzando la posizione e la posizione di destinazione inviata dal Server. Il giocatore che ha sparato il raggio laser in prima posizione dovrebbe ignorare questo evento altrimenti vedrà 2 laser.

  1. Crea un Script locale in StarterPlayerScripts chiamato ClientLaserManager .

  2. Dentro lo script, richiedi il modulo LaserRender .

  3. Crea una funzione chiamata createPlayerLaser con i parametri playerWhoShot , toolHandle e 1> endPosition1> .

  4. Connetti la funzione all'evento remoto LaserFired nella cartella Eventi.

  5. Nella funzione, usa un se statement to check if playerWhoShot does not equal the LocalPlayer.

  6. All'interno della dichiarazione if, chiama la funzione createLaser dal modulo LaserRender 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
    -- Mostra 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 iniziando un Serverlocale. Posiziona ciascun client su lati diversi del tuo 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 viene attualmente riprodotto solo sul client che spara il proiettile. Avrai bisogno di spostare il codice per riprodurlo in modo che altri giocatori possano ascoltarlo anche.

  1. Nell'script ToolController, naviga alla funzione toolActivated e rimuovi la linea che riproduce il suono di Attivazione.


    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. Nella parte inferiore della funzione createLaser in LaserRender, dichiara una variabile chiamata shootingSound e usa il metodo 2>Class.Instance:FindFirstChild()|FindFirstChild() di 5> toolHandle5> per controllare il suono 8> Attiva8>.

  3. Usa una if dichiarazione per controllare se shootingSound esiste; se lo fa, chiama la sua funzione Play .


    laserPart.Parent = workspace
    -- Aggiungi 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

Protezione dei telecomandi usando la convalida

Se il server non controlla i dati provenienti dalle richieste in arrivo, un hacker può abusare di funzioni e eventi remoti e usarli per inviare valori falsi al Server. È importante utilizzare la convalida lato server per prevenire questo.

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

La validazione è il processo di controllo che il server invia al server sono realistici. In questo caso, il server dovrà:

  • Controlla se la distanza tra il giocatore e la posizione colpita dal laser è all'interno di un certo limite.
  • Raycast tra l'arma che ha sparato il laser e la posizione di colpo per assicurarsi che il colpo fosse possibile e non passasse attraverso nessun muro.

Client

Il client deve inviare al server la posizione colpita dal raycast in modo che possa controllare la distanza che è realistica.

  1. In ToolController , fai clic sulla linea in cui viene eseguito l'evento remoto del personaggio danneggiante 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 remoto DamageCharacter, quindi il ServerLaserManager deve essere regolato per accettarlo.

  1. Nell'script ServerLaserManager, aggiungi un hitPosition parametro alla funzione damageCharacter.


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- Rimuovi la salute dal personaggio
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. Sotto la funzione getPlayerToolHandle, crea una funzione chiamata isHitValid con tre parametri: playerFired , 1> characterToDamage1> e 4> hitPosition4> .


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

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

  1. Dichiarare una variabile chiamata MAX_HIT_PROXIMITY nella parte superiore dello script e assegnarle un valore di 10 . Questa sarà la distanza massima consentita tra il colpo e il personaggio. È necessario un tolleranza poiché il personaggio potrebbe essere spostato leggermente dal momento che il client ha avviato 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 di colpo. Se la distanza è maggiore di MAX_HIT_PROXIMITY allora restituisci false .


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Validare la distanza tra il carattere colpito e la posizione di colpo
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > MAX_HIT_PROXIMITY then
    return false
    end
    end

Il secondo check coinvolgerà un raycast tra l'arma sparata e la posizione di colpo. Se il raycast restituisce un oggetto che non è il personaggio, puoi assumere che il colpo non sia valido poiché qualcosa stava bloccando il colpo.

  1. Copia il codice sottostante per eseguire questo Controllare /Verificare. Ritorna true alla fine della funzione: se raggiunge la Terminare, tutti i controlli sono passati.


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Validare la distanza tra il carattere colpito e la posizione di colpo
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 then
    return false
    end
    -- Controlla se sparare 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, ignorare il colpo
    if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
    return false
    end
    end
    return true
    end
  2. Dichiarare una variabile nella funzione damageCharacter chiamata validoSparo . Assegnare al risultato di una chiamata alla funzione isHitValid con tre argomenti: 2> playerFired2> , 5> characterToDamage5> e 8> hitPosition8> .

  3. Nella dichiarazione seguente, aggiungi un operatore e per controllare se validShot è vero .


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
    if humanoid and validShot then
    -- Rimuovi la salute dal personaggio
    humanoid.Health -= LASER_DAMAGE
    end
    end

Ora l'evento remoto del danno è più sicuro e impedirà la maggior parte dei giocatori di abusarci. Nota che alcuni giocatori malevoli troveranno spesso modi intorno alla validazione; mantenere gli eventi remoti sicuri è un sforzo continuo.

Il tuo laser blaster è ora completato, con un sistema di rilevamento dei colpi di base che utilizza il raycasting. Prova il tutorial Rilevamento dell'input dell'utente per scoprire come puoi aggiungere un'azione di ricarica al tuo laser blaster, o creare una mappa di gioco divertente e provare il tuo laser blaster 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 sia passato abbastanza tempo dal momento in cui è stato sparato l'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 del mouse 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- La direzione di unità del ray multiplicata da una distanza massima
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast dall'origine del re 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 di direzione normalizzato e moltiplica per la distanza laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direzione a cui sparare l'arma, moltiplicata da una distanza massima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignora il personaggio del Giocatoreper impedirgli di danneggiarsi
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 la posizione di fine
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- L'istanza colpita sarà un figlio di un modello di personaggio
-- Se viene trovato un humanoid nel modello, è 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 di fine raggio in base alla distanza laser massima
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)

Renderer laser


local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Tempo durante cui il laser è visibile
-- Crea un raggio laser dalla posizione di partenza verso una posizione di destinazione
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 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 LaserRenderer

Gestore Laser del Server


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)
-- Validare la distanza tra il carattere colpito e la posizione di colpo
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Controlla se sparare 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, ignorare il colpo
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Avvisa 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 la salute dal personaggio
humanoid.Health -= LASER_DAMAGE
end
end
-- Connetti eventi a 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
-- Mostra 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)