Rilevamento degli impatti con i 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 del giocatore e a rilevare se colpisce o meno un Giocatore.

Lancio di raggi per trovare collisioni

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

Raycast da A verso B che si scontra 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 by raycasting dalla posizione del mouse 2D del Giocatoresullo schermo direttamente in avanti dalla fotocamera nel Mondodel gioco.Il raggio si scontrerà con qualsiasi cosa il giocatore stia mirando con il Topo, or mouse as computer mouse.

  1. Apri lo script ToolController all'interno dello strumento Blaster da Crea strumenti del giocatore.Se non hai ancora completato quel tutorial puoi scaricare il modello 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 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 è nota, le sue proprietà X e Y possono essere utilizzate come parametri per la funzione Camera:ViewportPointToRay(), che crea un Ray dal monitor nel Mondodel gioco 3D.

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

È tempo 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, userai le proprietà di origine e direzione di screenToWorldRay .

La lunghezza del vector di direzione determina fino a che punto il raggio viaggerà.Il raggio deve essere lungo quanto il MAX_MOUSE_DISTANCE , quindi dovrai moltiplicare il veicolo di direzione per MAX_MOUSE_DISTANCE .

  1. Dichiara una variabile denominata 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 del mouse 2D
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Il vector di direzione dell'unità del raggio moltiplicato da una distanza massima
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
  2. Chiama la funzione Raycast dell'area di lavoro, 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 del mouse 2D
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Il vector di direzione dell'unità del raggio moltiplicato 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 di lancio del raggio trova un oggetto colpito dal raggio, restituirà un RaycastResult , che contiene informazioni sulla collisione tra il raggio e l'oggetto.

Proprietà RaycastResultDescrizione
IstanzaLa BasePart o Terrain cellula che il raggio ha intersecato.
PosizioneDove si è verificata l'intersezione; di solito un punto direttamente sulla superficie di una parte o di un terreno.
MaterialeIl materiale al punto di collisione.
NormaleIl normale vectore del volto intersecato. Questo può essere utilizzato per determinare in quale direzione punta il volto.

La proprietà Posizione sarà la posizione dell'oggetto su cui il mouse si sta muovendo.Se il mouse non si trova sopra qualsiasi oggetto a una distanza di MAX_MOUSE_DISTANCE , raycastResult sarà nil .

  1. Crea una dichiarazione if per controllare se raycastResult esiste.

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

  3. Se raycastResult è nil trova la fine del raycast.Calcola la posizione 3D del 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)
-- Il vector di direzione dell'unità del raggio moltiplicato 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 di intersezione 3D
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 del mouse 3D è nota, può essere utilizzata come posizione obiettivo per sparare un laser verso.Un secondo raggio può essere lanciato tra l'arma del Giocatoree la posizione target utilizzando la funzione Lancio di raggio .

  1. Dichiara una costante chiamata MAX_LASER_DISTANCE in cima allo script e assegnala a 500 , o alla 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 posizione del mouse . Questa sarà la posizione target 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 vector di direzione per la funzione raycast rappresenterà la direzione dalla posizione dello strumento del Giocatorealla posizione target.

  1. Dichiara una variabile con il nome targetDirection e calcola il veicolo direzionale sottraendo la posizione dello strumento da mouseLocation.

  2. Normalizza il vector utilizzando la sua Proprietà Unità . Questo gli dà una magnitudine di 1, il che rende più facile moltiplicarla in seguito.


    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    -- Calcola un vectore di direzione normalizzato e moltiplica per la distanza laser
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. Dichiara una variabile chiamata directionVector e assegnala a essa il moltiplicato dal >.


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

Un oggetto A RaycastParams può essere utilizzato per memorizzare parametri aggiuntivi per la funzione raycast.Verrà utilizzato nel tuo laser blaster per assicurarsi che il raycast non si imbatta accidentalmente con il giocatore che spara l'arma.Qualsiasi parte inclusa nella proprietà di un oggetto RaycastParams verrà 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 che contiene il personaggio locale del Giocatoree assegnalo alla Proprietà.

  3. Raycast dalla posizione della maniglia degli strumenti 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 vectore di direzione normalizzato e moltiplica per la distanza laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direzione per sparare l'arma moltiplicata da una distanza massima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignora il personaggio del Giocatoreper impedirgli di danneggiare se stesso
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 restituisca 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 nulla è stato restituito, la posizione finale deve essere calcolata per creare il laser.

  1. Dichiara una variabile vuota chiamata hitPosition .

  2. Usa una dichiarazione se 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)
    -- Verifica se sono stati colpiti oggetti tra la posizione di inizio e la fine
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. Se weaponRaycastResult non ha alcun 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)
    -- Verifica se sono stati colpiti oggetti tra la posizione di inizio e la fine
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    else
    -- Calcola la posizione finale in base alla distanza laser massima
    hitPosition = tool.Handle.Position + directionVector
    end
    end
  4. Naviga alla funzione toolActivated e chiama la funzione fireWeapon in modo che il laser spari ogni volta che viene attivato lo strumento.


    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 Giocatoreo è solo un pezzo di paesaggio, dovrai cercare un Humanoid , poiché ogni personaggio ne ha uno.

Prima, dovrai trovare il modello di carattere **** .Se una parte del personaggio è stata colpita, non puoi supporre che il genitore dell'oggetto colpito sarebbe il personaggio.Il laser potrebbe aver colpito una parte del corpo, un Accessorioo uno strumento, tutti i quali si trovano in diverse parti della gerarchia del personaggio.

Puoi usare FindFirstAncestorOfClass per trovare un antenato modello di personaggio dell'oggetto colpito dal laser, se ne esiste uno.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 alla dichiarazione weaponRaycastResult se per controllare se un carattere è stato colpito.


    -- Verifica se sono stati colpiti oggetti tra la posizione di inizio e la fine
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    -- L'istanza colpita sarà un figlio di un modello di personaggio
    -- Se un umanoide viene trovato 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 finale in base alla distanza laser massima
    hitPosition = tool.Handle.Position + directionVector
    end

Ora il laser blaster dovrebbe stampare Player hit nella finestra di output ogni volta che l'operazione di lancio del raggio colpisce un altro Giocatore.

Test con più giocatori

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

  1. Seleziona la scheda Test in Studio.

  2. Assicurati che il menu a discesa dei giocatori sia impostato su '2 Giocatori' e clicca il pulsante Inizia per iniziare un server locale con 2 client.Tre finestre appariranno.La prima finestra sarà il Serverlocale, le altre finestre saranno i client per Player1 e Player2.

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

Puoi scoprire di più sulla scheda Test qui .

Trova la posizione del laser

Il blaster dovrebbe sparare un raggio rosso di luce al suo bersaglio.La funzione per questo sarà all'interno di un ModuleScript quindi può essere riutilizzata in altri script in seguito.Prima, lo script dovrà trovare la posizione in cui il raggio laser deve essere reso.

  1. Crea un ModuleScript chiamato LaserRenderer , parented a StarterPlayerScripts sotto StarterPlayer.

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

  3. Dichiara una variabile con il nome SHOT_DURATION con un valore di 0.15 .Questa sarà la quantità di tempo (in secondi) durante 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 durante 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 con il nome startPosition e imposta la proprietà Posizione di toolHandle come suo valore.Questa sarà la posizione del laser blaster del Giocatore.

  6. Dichiara una variabile chiamata laserDistance e sottrai endPosition da startPosition per trovare la differenza tra i due veicoli.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 a startPosition e rivolto verso endPosition .Moltiplica questo con un nuovo CFrame con un valore dell'asse Z di metà del negativo 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 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 assegnala a essa una nuova esempio.

  2. Imposta le seguenti proprietà di laserPart :

    1. Dimensione : Vector3.new(0.2, 0.2, distanza laser)
    2. CFrame : laserCFrame
    3. Ancorato : vero
    4. CanCollide : false
    5. Colore : Color3.fromRGB(225, 0, 0) (un forte colore rosso)
    6. Materiale : Enum.Material.Neon
  3. Genitore laserPart a Workspace .

  4. Aggiungi la parte al servizio Debris in modo che venga rimossa dopo la quantità 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 un raggio laser al servizio Debris da rimuovere e pulire
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

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

  1. All'apice dello script ToolController , dichiara una variabile con il nome LaserRenderer e richiedi il ModuloScript 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. Alla fine della funzione fireWeapon della funzione, chiama la funzione LaserRenderer createLaser utilizzando il pulsante dell'oggetto e hitPosition come argomenti.


    -- Calcola la posizione finale 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 Gioca. Un raggio laser dovrebbe essere visibile tra l'arma e il mouse quando lo strumento è attivato.

Votareil tasso di fuoco dell'arma

Le armi hanno bisogno di un ritardo tra ogni colpo per impedire ai giocatori di infliggere troppo danno in un breve periodo di tempo.Questo può essere controllato verificando se è trascorso abbastanza tempo da quando un giocatore ha ultimamente sparato.

  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'ultima volta che 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
    -- Verifica se sia trascorso abbastanza tempo dall'ultimo colpo sparato
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. All'interno della funzione dichiara una variabile con il nome currentTime ; assegna al risultato della chiamata della funzione tick().Questo restituisce quanto tempo è trascorso, in secondi, dal 1° gennaio 1970 (una data arbitraria ampiamente utilizzata per calcolare il tempo).

  5. Sottrai il timeOfPreviousShot da currentTime e restituisci falso se il risultato è più piccolo di FIRE_RATE ; altrimenti, restituisci vero .


    -- Verifica se sia trascorso abbastanza tempo dall'ultimo colpo sparato
    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 utilizzando tick .


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. All'interno della funzione toolActivated, crea una dichiarazione se e chiama canShootWeapon per verificare 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 da quanto velocemente fai clic, ci sarà sempre un breve ritardo di 0,3 secondi tra ogni colpo.

Danni al Giocatore

I client non possono danneggiare altri client direttamente; il server deve essere responsabile per emettere danni quando un giocatore viene colpito.

I client possono utilizzare un RemoteEvent per dire al server che un personaggio è stato colpito.Queste devono essere memorizzate in ReplicatedStorage , dove sono visibili sia al client che al Server.

  1. Crea una Cartella in ReplicatedStorage chiamata Eventi .

  2. Inserisci un RemoteEvent nella cartella Eventi e nominalo DannoCarattere .

  3. In ToolController , crea le 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 dichiarazione di stampa con una linea di Luau per attivare l'evento remoto DamageCharacter con la variabile 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 finale in base alla distanza laser massima
    hitPosition = tool.Handle.Position + directionVector
    end

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

  1. Inserisci uno Script nel ServerScriptService e nominalo ServerLaserManager .

  2. Dichiara una variabile chiamata LASER_DAMAGE e impostala su 10 o su 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'Humanoide del personaggio e sottrai LASER_DAMAGE dalla sua salute.

  5. Connetti la funzione damageCharacter alla funzione remota DamageCharacter nell'archiviazione eventi della 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
    -- Collega eventi a funzioni appropriate
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. Prova il blaster con 2 giocatori avviando un Serverlocale.Quando spari all'altro Giocatore, la sua salute diminuirà del numero assegnato a LASER_DAMAGE .

Rendi i raggi laser di altri Giocatore

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 è 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ò significherebbe che il client lanciando l'arma vedrebbe un ritardo tra quando attiva l'arma e quando vede il raggio laser; l'arma si sentirebbe laggosa come Risultato.

Per risolvere questo problema, ogni client creerà i propri beam laser.Questo significa che il client che spara l'arma vedrà il raggio laser immediatamente.Altri clienti sperimenteranno un piccolo ritardo tra quando un altro giocatore spara e appare un raggio.Questo è il miglior caso scenario: non c'è modo per comunicare il laser di un cliente ad altri clienti più velocemente.

Cliente dello sparatore

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

  1. Inserisci un Evento remoto nella cartella Eventi in ReplicatedStorage e nominalo LaserFired .

  2. Individua la funzione fireWeapon nella script ToolController .Alla fine della funzione, lancia l'evento remoto LaserFired utilizzando 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 deve ora ricevere l'evento che il client ha lanciato e dire a tutti i clienti la posizione iniziale e finale del raggio laser in modo che possano renderlo 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 .


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

    -- Collega eventi a funzioni appropriate
    eventsFolder.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 dove possibile.La posizione della maniglia dell'arma del personaggio sarà la posizione di partenza, 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 il personaggio del Giocatoreper l'arma e restituisci l'oggetto handle.


    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 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 (quindi il client per quel giocatore non rende il laser due volte), la maniglia 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, lancia l'evento LaserFired per tutti i client che utilizzano playerFired , toolHandle e endPosition come argomenti.


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

Render sul client

Ora FireAllClients è stato chiamato, ogni client riceverà un evento dal server per rendere un raggio laser.Ogni client può riutilizzare il modulo LaserRenderer precedente per rendere il raggio laser utilizzando la posizione della maniglia e la posizione finale del strumentoinviata dal Server.Il giocatore che ha lanciato il raggio laser in primo luogo 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 Eventi.

  5. Nella funzione, usa una dichiarazione se per controllare se non è uguale a il LocalPlayer.

  6. All'interno della dichiarazione 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
    -- 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 avviando un Serverlocale.Posiziona ciascun cliente 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 si riproduce solo sul client che sta sparando il proiettile.Dovrai spostare il codice per riprodurre il suono in modo che anche gli altri giocatori lo sentano.

  1. Nello script ToolController , naviga alla funzione toolActivated e rimuovi la linea che riproduce il suono Attiva.


    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. Nella parte inferiore della funzione createLaser in LaserRenderer , dichiara una variabile chiamata shootingSound e usa il metodo FindFirstChild() di toolHandle per controllare il suono Attiva .

  3. Usa una dichiarazione se per controllare se shootingSound esiste; se esiste, chiama la sua funzione Gioca .


    laserPart.Parent = workspace
    -- Aggiungi un raggio laser al servizio Debris da rimuovere e pulire
    Debris:AddItem(laserPart, SHOT_DURATION)
    -- Riproduci il suono di sparo dell'arma
    local shootingSound = toolHandle:FindFirstChild("Activate")
    if shootingSound then
    shootingSound:Play()
    end
    end

Remoti sicuri utilizzando la convalida

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

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

La convalida è il processo di verifica che i valori inviati al server sono realistici. In questo caso, il server dovrà:

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

Cliente

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

  1. In ToolController , naviga alla linea in cui viene eseguito l'evento remoto DamageCharacter 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 aggiustato per accettarlo.

  1. Nello script ServerLaserManager, aggiungi un parametro > alla funzione >.


    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, 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 .Questa sarà la distanza massima consentita tra il colpo e il personaggio.È necessaria una tolleranza perché il personaggio potrebbe essere stato spostato leggermente dal momento che il client ha lanciato 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 falso .


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Validare la distanza tra il personaggio colpito e la posizione del colpo
    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 restituisce un oggetto che non è il personaggio, puoi supporre che il colpo non sia stato valido poiché qualcosa bloccava il colpo.

  1. Copia il codice qui sotto per eseguire questa Controllare /Verificare. Restituisci vero alla fine della funzione: se raggiunge la Terminare, tutte le verifiche sono passate.


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Validare la distanza tra il personaggio colpito e la posizione del colpo
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 then
    return false
    end
    -- Verifica se spara attraverso le pareti
    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. Dichiara una variabile nella funzione damageCharacter chiamata validShot .Assegnagli il risultato di una chiamata alla funzione isHitValid con tre argomenti: playerFired , characterToDamage e hitPosition .

  3. Nella dichiarazione if 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 character danno è più sicuro e impedirà alla maggior parte dei giocatori di abusarne.Si noti che alcuni giocatori malevoli troveranno spesso modi per aggirare la validazione; mantenere sicuri gli eventi remoti è uno sforzo continuo.

Il tuo laser blaster è ora completo, con un sistema di rilevamento dei colpi di base utilizzando il raycasting.Prova il tutorial Rilezione 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 codice

Controllore di strumenti


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
-- Verifica se sia trascorso abbastanza tempo dall'ultimo colpo sparato
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)
-- Il vector di direzione dell'unità del raggio moltiplicato da una distanza massima
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast dall'origine del roy verso la sua direzione
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Restituisci il punto di intersezione 3D
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 vectore di direzione normalizzato e moltiplica per la distanza laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- La direzione per sparare l'arma, moltiplicata da una distanza massima
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignora il personaggio del Giocatoreper impedirgli di danneggiare se stesso
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Verifica se sono stati colpiti oggetti tra la posizione di inizio e la fine
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- L'istanza colpita sarà un figlio di un modello di personaggio
-- Se un umanoide viene trovato 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 finale 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)

LaserRenderer


local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Tempo durante 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 un raggio laser al servizio Debris da rimuovere e pulire
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

Manager 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 personaggio colpito e la posizione del colpo
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Verifica se spara attraverso le pareti
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
-- Collega 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)