使用雷射檢測目標

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

在這個教學中,您將學習如何從 創建玩家工具 中將雷射投射到玩家身上,以及是否擊中玩家。

投射以找到碰撞

射線投射 從起始位置向給定方向創建一個隱形射線,長度已定義。如果光線與路徑上的物體或地形發生碰撞,它會返回碰撞的信息,例如位置和與之碰撞的物體。

由 A 向 B 射線撞擊牆壁

找到鼠標位置

在雷射可以射擊之前,您必須先知道玩家正在瞄準哪裡。這可以通過從玩家的 2D 鼠標位置在屏幕上直接射出到遊戲世界來找到。射線將與玩家用滑鼠瞄準的任何東西碰撞。

  1. 創建玩家工具 內開啟 工具控制器 腳本,從 Blaster 工具中。如果你尚未完成該教學,你可以下載 爆破器 模型並將其插入起動包。

  2. 在腳指令碼頂部,宣言一個名為 MAX_MOUSE_DISTANCE 的常量,值為 1000

  3. 創建一個名為 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
    -- 將事件連接到適當的功能
    tool.Equipped:Connect(toolEquipped)
    tool.Activated:Connect(toolActivated)
  4. 使用 UserInputService 的 GetMouseLocation 功能來獲得玩家的 2D 鼠標位置在屏幕上。將它指派給名為 mouseLocation 的變量。


    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    end

現在 2D 滑鼠位置已知,其 XY 屬性可以用作參數為 Camera:ViewportPointToRay() 函數,創建從屏幕到 3D 遊戲世界的 Ray

  1. 使用 XY 屬性的 mouseLocation 作為 ViewportPointToRay() 函數的參數。將此分配給名為 screenToWorldRay 的變量。


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- 從 2D 滑鼠位置創建一個光束
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    end

是時候使用 Raycast 函數來檢查射線是否擊中對物件了。這需要一個起始位置和方向向向量力:在這個例子中,你將使用 screenToWorldRay 的起源和方向屬性。

方向向量的長度決定光束會移動多遠。射線需要和 MAX_MOUSE_DISTANCE 一樣長,因此您必須將方向向量乘以 MAX_MOUSE_DISTANCE

  1. 宣言一個名為 directionVector 的變量,並將其值設為 screenToWorldRay.Direction 乘以 MAX_MOUSE_DISTANCE 的值。


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- 從 2D 滑鼠位置創建一個光束
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- 射線的單位方向向量乘以最大距離
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
  2. 呼叫工作區的 Raycast 函數,傳遞 起源 屬性的 screenToWorldRay 作為第一個參數和 directionVector 作為第二個參數。將此分配給名為 raycastResult 的變量。


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- 從 2D 滑鼠位置創建一個光束
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- 射線的單位方向向量乘以最大距離
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
    -- 從射線的起源向其方向射出射線
    local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)

碰撞資訊

如果射線投射操作找到被射線擊中的對象,它將返回一個 RaycastResult ,包含有關射線和對物件碰撞的信息。

射線投射結果屬性說明
實例射線交叉的 BasePartTerrain 細胞。
位置交叉點發生的地方;通常是一個點直接在零件或地形表面上。
材料碰撞點的材料。
正常交叉面的正常向量。這可以用來確定面向哪一方。

位置 屬性將是滑鼠漂浮的對象位置。如果滑鼠未遊過距離 MAX_MOUSE_DISTANCE 內的任何物件,raycastResult將是nil

  1. 創建一個if聲明來檢查raycastResult是否存在。

  2. 如果 raycastResult 有值,返回其 位置 屬性。

  3. 如果 raycastResultnil 則找到射線投射的結束。通過添加 screenToWorldRay.OrigindirectionVector 來計算鼠標的 3D 位置。


local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- 從 2D 滑鼠位置創建一個光束
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- 射線的單位方向向量乘以最大距離
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- 從射線的起源向其方向射出射線
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- 返回 3D 交點
return raycastResult.Position
else
-- 沒有擊中任何物件,因此在光束末端計算位置
return screenToWorldRay.Origin + directionVector
end
end

向目標發射

現在 3D 滑鼠位置已知,它可以用作 目標位置 來發射雷射。使用 射線投射 功能可以將第二束射線投射到玩家的武器和目標位置之間。

  1. 在腳本頂部宣言一個名為 MAX_LASER_DISTANCE 的常量,並將其指派給 500 或你選擇的雷射爆破範圍。


    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  2. 火焰武器 功能下創建一個名為 getWorldMousePosition 的功能。

  3. 呼叫 getWorldMousePosition 並將結果指派給名為 mousePosition 的變量。這將是射線投射的目標位置。


    -- 沒有擊中任何物件,因此在光束末端計算位置
    return screenToWorldRay.Origin + directionVector
    end
    end
    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end

這次,射線投射功能的方向向量將代表從玩家工具位置到目標位置的方向。

  1. 宣言名為 targetDirection 的變量,並將工具位置從 mouseLocation 減去以計算方向向量。

  2. 使用其 單位 屬性來正常化向量。這會給它一個值為 1 的大小,這使得以後很容易乘以長度。


    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    -- 計算正常方向向量並乘以雷射距離
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. 宣言一個名為 directionVector 的變量,並將其乘以 後分配給它。


    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    -- 武器發射的方向,乘以最大距離
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

一個 RaycastParams 對象可用於儲存射線投射功能的額外參數。它將用於你的雷射爆破器,以確保射線不會意外地與發射武器的玩家碰撞。任何包含在 屬性的 RaycastParams 對象中的零件將在射線投射中被忽略。

  1. 繼續 fireWeapon 函數並宣言一個名為 weaponRaycastParams 的變量。將新的 RaycastParams 對象分配給它。

  2. 創建一個包含玩家本地 角色 的表,並將其分配給 weaponRaycastParams.FilterDescendantsInstances 屬性。

  3. 從玩家的工具處理位置射出光束,朝 directionVector 的方向。記得這次添加 weaponRaycastParams 作為參數。將它指派給名為 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()
-- 計算正常方向向量並乘以雷射距離
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- 武器發射的方向乘以最大距離
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- 忽略玩家的角色,以防止他們傷害自己
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end

最後,你需要檢查射線投射操作返回了值。如果值被返回,對象被射線擊中了,並且可以在武器和擊中位置之間創建一個雷射。如果沒有返回任何東西,最終位置需要計算,以創建雷射。

  1. 宣言一個名為 hitPosition 的空變量。

  2. 使用 如果 聲明來檢查 weaponRaycastResult 是否有值。如果對象被擊中,將 weaponRaycastResult.Position 分配給 hitPosition .


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- 檢查是否有物體在開始和結束位置之間被擊中
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. 如果 沒有值,則通過將工具處理器的 位置 與 》 加起來,計算射線投射的終端位置。將它指派給 命中位置


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- 檢查是否有物體在開始和結束位置之間被擊中
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    else
    -- 根據最大激光距離計算最終位置
    hitPosition = tool.Handle.Position + directionVector
    end
    end
  4. 導航到 toolActivated 函數並呼叫 fireWeapon 函數,以便激光每次啟用工具時發射。


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

檢查對象命中

若要查找激光擊中的物體是屬於玩家角色的一部分還是只是風景的一部分,你需要尋找 Humanoid ,因為每個角色都有一個。

首先,你需要找到 角色模型 。如果字元的一部分被擊中,你不能假設被擊中的對象的父親會是字元。雷射可能擊中了角色的身體部位、飾品或工具,這些都位於不同部分的角色層次中。

您可以使用 FindFirstAncestorOfClass 來查找激光擊中對象的角色模型祖先,如果存在。如果你找到一個模型,包含人形,在大多數情況下,你可以假設它是一個角色。

  1. 將下列突出代碼添加到 weaponRaycastResult 如果 聲明中,以檢查是否有字符被擊中。


    -- 檢查是否有物體在開始和結束位置之間被擊中
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    -- 命中的實例將是角色模型的子孫
    -- 如果在模型中找到非人形,則可能是玩家的角色
    local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    print("Player hit")
    end
    end
    else
    -- 根據最大激光距離計算最終位置
    hitPosition = tool.Handle.Position + directionVector
    end

現在雷射爆破器應該每次射線投射操作擊中另一名玩家時,將 Player hit 列印到輸出窗口。

與多名玩家進行測試

需要兩名玩家來測試武器射線是否找到其他玩家,因此您需要啟動本地伺服器。

  1. 在 Studio 中選擇 測試 標籤。

  2. 請確保玩家下拉選單設為「2 名玩家」,然後點擊開始按鈕以 開始 一個本地伺服器並配備 2 個客戶端。三個窗口會出現。第一個窗口將是本地伺服器,其他窗口將是玩家1和玩家2的客戶。

  3. 在一台客戶端上,點擊另一名玩家使用武器進行測試射擊。「玩家命中」應在每次射擊玩家時顯示在輸出中。

您可以在此 測試 標籤 了解更多信息

找到雷射位置

爆破器應向目標發射紅色光束。這個功能會在 ModuleScript 內,因此可以稍後在其他腳本中重複使用。首先,腳本需要找到激光束應該渲染的位置。

  1. 創建名為 LaserRenderer 的模組腳本,其父處為 StarterPlayerScripts 下的 StarterPlayer。

  2. 開啟腳本並將模組表重命名為腳本名稱 LaserRenderer

  3. 宣言名為 SHOT_DURATION 的變量,值為 0.15 。這將是雷射可見的時間(以秒為單位)。

  4. 創建一個名為 createLaser 的 LaserRenderer 功能,具有兩個參數 toolHandleendPosition


    local LaserRenderer = {}
    local SHOT_DURATION = 0.15 -- 雷射可見的時間
    -- 從起始位置向終止位置創建雷射光束
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. 宣言名為 startPosition 的變量,並將 位置 屬性設為 toolHandle 的值。這將是玩家的雷射發射器的位置。

  6. 宣言名為 laserDistance 的變量,並從 endPosition 中減去 startPosition 以找出兩個向量之間的差異。使用此 強度 屬性來獲得激光束的長度。


    function LaserRenderer.createLaser(toolHandle, endPosition)
    local startPosition = toolHandle.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    end
  7. 宣言 laserCFrame 變量來儲存雷射光束的位置和方向。位置需要是光束的起點和終點的中點。使用 CFrame.lookAt 來創建一個新的 CFrame 位置在 startPosition 並面向 endPosition 。將此乘以新的 CFrame,其 Z 軸值為負值的一半 laserDistance 以獲得中點。


    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

創建雷射零件

現在您知道在哪裡製作雷射光束,您需要添加光束本身。這可以使用螢光零件輕鬆完成。

  1. 宣言變量 laserPart 並為它指派新的 Part 個體、實例。

  2. 設定 laserPart 的下列屬性:

    1. 尺寸 : Vector3.new(0.2, 0.2, laserDistance)
    2. CFrame :laserCFrame
    3. 已錨定 : true
    4. 可否碰撞 : false
    5. 顏色 : Color3.fromRGB(225, 0, 0) (強烈的紅色)
    6. 材料 : Enum.Material.Neon
  3. laserPart工作區

  4. 將零件添加到 Debris 服務,以便在 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
    -- 將雷射光束添加到垃圾服務中,以便被移除和清理
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

現在渲染雷射光束的功能已完成,可以由 工具控制器 呼叫。

  1. 工具控制器 指令碼本的頂部,宣言一個名為 LaserRenderer 的變量,並需要位於 PlayerScripts 中的 LaserRenderer 模組腳本。


    local UserInputService = game:GetService("UserInputService")
    local Players = game:GetService("Players")
    local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
    local tool = script.Parent
  2. fireWeapon 函數底部,使用工具處理和 createLaser 作為參數來呼叫 LaserRenderer hitPosition 函數。


    -- 根據最大激光距離計算最終位置
    hitPosition = tool.Handle.Position + directionVector
    end
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  3. 點擊「玩」按鈕測試武器。當工具啟用時,應該在武器和滑鼠之間看到一個雷射光束。

控制武器射評分

武器需要在每次射擊之間延遲,以防玩家在短時間內造成過多傷害。這可以通過檢查玩家最後發射時間已經過足夠長的時間來控制。

  1. 工具控制器 頂部宣言一個變量,稱為 FIRE_RATE 。這將是每次射擊之間的最小時間。給予它你選擇的值;這個例子使用 0.3 秒。

  2. 宣言另一個變量在下方稱為 timeOfPreviousShot 的值為 0 。這裡存儲玩家最後一次開火的時間,並將在每一次射擊中更新。


    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 300
    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
  3. 創建一個名為 canShootWeapon 的功能,沒有參數。這個功能會看看從上一次射擊以來已經過了多少時間,並返回真實或虛假。


    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
    -- 檢查自上次射擊以來已經過足夠時間了否
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. 在函數內宣言一個名為 currentTime 的變量;將其指派給 tick() 函數的結果。這會返回自 1970 年 1 月 1 日起,在秒數中已經過去的時間(一個隨意使用的日期,廣泛用於計算時間)。

  5. timeOfPreviousShot 中減去 currentTime 並返回 如果結果小於 FIRE_RATE 否則返回


    -- 檢查自上次射擊以來已經過足夠時間了否
    local function canShootWeapon()
    local currentTime = tick()
    if currentTime - timeOfPreviousShot < FIRE_RATE then
    return false
    end
    return true
    end
  6. fireWeapon 函數結束時,每次使用 tick 發射武器時,更新 timeOfPreviousShot


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. toolActivated 函數內,創建一個 如果 聲明並呼叫 canShootWeapon 來檢查武器是否可以發射。


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

當您測試爆破器時,您應該發現無論您點擊多快,每次射擊之間總是會有短暫的 0.3 秒延遲。

傷害玩家

客戶無法直接傷害其他客戶;服務器需要在玩家被擊中時負責發出傷害。

客戶端可以使用 RemoteEvent 告訴服務器,一個字符已被擊中。這些應該存儲在 複製存儲 中,客戶端和伺服器都能看到它們。

  1. 在 ReplicatedStorage 中創建一個 文件夾 以名為 事件 的名稱。

  2. 將遠端事件插入事件文件夾,並將名稱命名為 傷害角色

  3. 工具控制器 中,在腳本開始時創建變量以用於 ReplicatedStorage 和事件文件夾。


    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. fireWeapon 中用 Luau 行替換 "Player hit" 列印聲明,將 傷害角色 遠端事件發射到 characterModel 變量作為參引數。


    local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    eventsFolder.DamageCharacter:FireServer(characterModel)
    end
    end
    else
    -- 根據最大激光距離計算最終位置
    hitPosition = tool.Handle.Position + directionVector
    end

伺服器需要對被擊中的玩家造成傷害,當事件發射時。

  1. 腳本 插入到 ServerScriptService 並命名為 ServerLaserManager

  2. 宣言名為 LASER_DAMAGE 的變量,並將其設為 10 或您選擇的值。

  3. 創建名為 damageCharacter 的功能,並將兩個參數稱為 playerFiredcharacterToDamage

  4. 在功能內,尋找角色的人形並從其生命值中扣除 LASER_DAMAGE

  5. damageCharacter 功能連接到事件文件夾中的 傷害角色 遠端事件。


    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
    -- 從角色中移除生命值
    humanoid.Health -= LASER_DAMAGE
    end
    end
    -- 將事件連接到適當的功能
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. 啟動本地伺服器以測試爆破器與 2 名玩家。當您射擊另一名玩家時,他的生命值會減少到指定的數字 LASER_DAMAGE

渲染其他玩家的雷射光束

目前,雷射光束由發射武器的客戶創建,因此只有他們才能看到雷射光束。

如果雷射光在伺服器上創建,那麼每個人都可以看到它。然而,客戶端射擊武器和服務器收到射擊信息之間會有一小點延遲 延遲 。這意味著客戶射擊武器時會看到從啟動武器到看到雷射光束之間的延遲;武器將因結果感覺到延遲。

為了解決這個問題,每個客戶都會創建自己的雷射光束。這意味著客戶射擊武器會立即看到雷射光束。其他客戶端會在另一名玩家射擊和光束出現之間經歷一小時間延遲。這是最好的情況:沒有辦法讓一個客戶的雷射快速傳送給其他客戶。

射手的客戶

首先,客戶端需要告訴服務器它已發射了雷射並提供終端位置。

  1. 在 ReplicatedStorage 的事件文件夾中插入 遠端事件 ,並將其命名為 LaserFired

  2. fireWeapon 指令碼本中找到 **** 功能。在函數結束時,使用 LaserFired 遠端事件發射 hitPosition 作為參引數。


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    eventsFolder.LaserFired:FireServer(hitPosition)
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end

服務伺服器

伺服器現在必須接收客戶端發射的事件,並告知所有客戶端雷射光束的起始和終點位置,以便他們也可以渲染它。

  1. 伺服器雷射管理器 指令碼本中,創建名為 playerFiredLaser 的函數,上面的 damageCharacter 與兩個參數稱為 playerFiredendPosition

  2. 將功能連接到 LaserFired 遠端事件。


    -- 通知所有客戶發射了雷射,讓他們能夠顯示雷射
    local function playerFiredLaser(playerFired, endPosition)
    end

    -- 將事件連接到適當的功能
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
    eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

伺服器需要雷射的起始位置。這可以從客戶端發送,但最好避免在可能的情況下信任客戶端。角色的武器握把位置將是起始位置,因此服務器可以從那裡找到它。

  1. 創建一個函數 getPlayerToolHandleplayerFiredLaser 函數上方帶有一個參數名為 player 的參數。

  2. 使用以下代碼搜尋玩家的角色以找到武器,並返回握把對物件。


    local LASER_DAMAGE = 10
    -- 找到玩家持有的工具處理器的處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器虿
    local function getPlayerToolHandle(player)
    local weapon = player.Character:FindFirstChildOfClass("Tool")
    if weapon then
    return weapon:FindFirstChild("Handle")
    end
    end
    -- 通知所有客戶發射了雷射,讓他們能夠顯示雷射
    local function playerFiredLaser(playerFired, endPosition)

服務器現在可以呼叫 FireAllClientsLaserFired 遠端事件上,將需要傳送給客戶的激光渲染信息發送給客戶。這包括發射激光的 玩家 (因此該玩家的客戶端不會兩次渲染激光)、爆破器的 手柄 (作為激光的起始位置) 和激光的最後位置。

  1. playerFiredLaser 函數中,以 getPlayerToolHandle 作為參數呼叫 playerFired 函數,並將值分配給名為 toolHandle 的變量。

  2. 如果 工具處理 存在,使用 playerFiredtoolHandleendPosition 作為參數的所有客戶都會發射 LaserFired 事件。


    -- 通知所有客戶發射了雷射,讓他們能夠顯示雷射
    local function playerFiredLaser(playerFired, endPosition)
    local toolHandle = getPlayerToolHandle(playerFired)
    if toolHandle then
    eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
    end
    end

在客戶端上渲染

現在 呼叫了所有客戶 ,每個客戶都會從伺服器接收一個事件來渲染雷射光束。每個客戶端可以從以前的 LaserRenderer 模組中重複使用,以使用工具的處理位置和結束位置值將雷射光束渲染到服務器所傳送的位置。發射雷射光束的玩家首先應該忽略此事件,否則他們會看到 2 個雷射。

  1. 在新手玩家腳本中創建 本地腳本 ,命名為 ClientLaserManager

  2. 在腳指令碼內,需要 LaserRenderer 模組。

  3. 創建名為 createPlayerLaser 的功能,參數為 playerWhoShottoolHandleendPosition

  4. 將功能連接到事件文件夾中的 LaserFired 遠端事件。

  5. 在功能中,使用 如果 聲明來檢查是否 不等於 LocalPlayer >

  6. 在 if 聲明內,使用 createLaserendPosition 來呼叫 LaserRenderer 模組的 toolHandle 函數作為參數。


    local Players = game:GetService("Players")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))
    local eventsFolder = ReplicatedStorage.Events
    -- 顯示另一名玩家的雷射
    local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
    if playerWhoShot ~= Players.LocalPlayer then
    LaserRenderer.createLaser(toolHandle, endPosition)
    end
    end
    eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
  7. 啟動本地伺服器以測試爆破器與 2 名玩家。將每個客戶放置在監視器的不同側面上,以便您一次看到兩個窗口。當您在一個客戶上射擊時,您應該在另一個客戶上看到激光。

聲音效果

射擊音效目前只在射出彈藥的客戶端播放。您需要將代碼移動到播放聲音,以便其他玩家也能聽到。

  1. 工具控制器 指令碼本中,導航到 工具啟用 功能,並移除播放啟用聲音的線。


    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. 在 LaserRenderer 的底部,宣言一個名為 shootingSound 的變量,並使用 方法的 檢查是否有 啟用 聲音。

  3. 使用 如果 聲明來檢查是否存在 shootingSound;如果存在,請呼叫其 播放 功能。


    laserPart.Parent = workspace
    -- 將雷射光束添加到垃圾服務中,以便被移除和清理
    Debris:AddItem(laserPart, SHOT_DURATION)
    -- 播放武器的射擊聲
    local shootingSound = toolHandle:FindFirstChild("Activate")
    if shootingSound then
    shootingSound:Play()
    end
    end

使用驗證來安全遠端

如果伺服器沒有檢查來自進入要求的數據,駭客可以濫用遠端功能和事件,並使用它們來向伺服器發送假值。使用 服務器端驗證 很重要,以防止這種情況。

在目前的形式下, 傷害角色 遠端事件非常脆弱,容易受到攻擊。駭客可以使用此事件對遊戲中的任何玩家造成傷害,而不需要射擊他們。

驗證是檢查服務器收到的值是否實際的過程。在這種情況下,服務器需要:

  • 檢查玩家和雷射擊中位置之間的距離是否在某個範圍內。
  • 在發射雷射的武器和命中位置之間投射光束,以確保射擊是可行的且沒有穿過任何牆壁。

客戶

客戶端需要向服務器發送射線投射所擊位置,以便它可以檢查距離是否合理。

  1. 工具控制器 中,導航到在 fireWeapon 函數中發射傷害角色遠端事件的線。

  2. 添加 hitPosition 作為參引數。


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

服務器

客戶端現在通過傷害角色遠端事件發送額外參數,因此 伺服器雷射管理器 需要調整以接受它。

  1. 伺服器雷射管理器 指令碼本中,將 hitPosition 參數添加到 damageCharacter 函數。


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- 從角色中移除生命值
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. getPlayerToolHandle 函數下,創建名為 isHitValid 的函數,具有三個參數:playerFiredcharacterToDamagehitPosition


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

第一次檢查將是從命中位置到角色命中位置的距離。

  1. 在腳本頂部宣言一個名為 MAX_HIT_PROXIMITY 的變量,並將其值設為 10 。這將是允許的最大距離,在命中和角色之間。需要容差,因為字元可能在客戶發射事件後稍微移動了。


    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    local MAX_HIT_PROXIMITY = 10
  2. isHitValid 函數中,計算角色和命中位置之間的距離。如果距離大於 MAX_HIT_PROXIMITY 則返回


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- 驗證角色命中和命中位置之間的距離
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > MAX_HIT_PROXIMITY then
    return false
    end
    end

第二次檢查會涉及武器發射和命中位置之間的射線投射。如果射線投射返回不是角色的對象,你可以假設射擊無效,因為有東西阻擋了射擊。

  1. 複製下面的代碼以執行此檢查。在函數結尾返回 true :如果到達結結束,所有檢查都通過了。


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- 驗證角色命中和命中位置之間的距離
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 then
    return false
    end
    -- 檢查是否穿過牆壁射擊
    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)
    -- 如果被擊中的實例不是角色,則忽略射擊
    if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
    return false
    end
    end
    return true
    end
  2. damageCharacter 函數中宣言一個變量,稱為 validShot 。將三個參數傳給 isHitValid 函數的呼叫結果分配給它:playerFiredcharacterToDamagehitPosition

  3. 在以下 if 聲明中,添加 運作符以檢查 validShot 是否為 true


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
    if humanoid and validShot then
    -- 從角色中移除生命值
    humanoid.Health -= LASER_DAMAGE
    end
    end

現在傷害角色遠端事件更安全,將防止大多數玩家濫用它。請注意,有些惡意玩家會經常找到逃避驗證的方法;保護遠端事件安全是一項持續努力。

您的雷射爆破器現已完成,使用射線投射的基本擊中偵測系統。試試偵測使用者輸入教學,找出如何將重新載入行動添加到你的雷射爆破器,或創建一個有趣的遊戲地圖,並試用你的雷射爆破器與其他玩家一起!

最終代碼

工具控制器


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
-- 檢查自上次射擊以來已經過足夠時間了否
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()
-- 從 2D 滑鼠位置創建一個光束
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- 射線的單位方向向量乘以最大距離
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- 從 roy 的起源向其方向射出光束
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- 返回 3D 交點
return raycastResult.Position
else
-- 沒有擊中任何物件,因此在光束末端計算位置
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- 計算正常方向向量並乘以雷射距離
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- 武器發射的方向,乘以最大距離
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- 忽略玩家的角色,以防止他們傷害自己
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- 檢查是否有物體在開始和結束位置之間被擊中
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- 命中的實例將是角色模型的子孫
-- 如果在模型中找到非人形,則可能是玩家的角色
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
-- 根據最大激光距離計算最終位置
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)

雷射渲染器


local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- 雷射可見的時間
-- 從起始位置向終止位置創建雷射光束
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
-- 將雷射光束添加到垃圾服務中,以便被移除和清理
Debris:AddItem(laserPart, SHOT_DURATION)
-- 播放武器的射擊聲
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer

伺服器雷射管理器


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- 找到玩家持有的工具處理器的處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器處理器虿
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)
-- 驗證角色命中和命中位置之間的距離
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- 檢查是否穿過牆壁射擊
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)
-- 如果被擊中的實例不是角色,則忽略射擊
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- 通知所有客戶發射了雷射,讓他們能夠顯示雷射
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
-- 從角色中移除生命值
humanoid.Health -= LASER_DAMAGE
end
end
-- 將事件連接到適當的功能
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

客戶雷射管理器


local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- 顯示另一名玩家的雷射
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)