คู่มือการใช้งานระดับกลาง

การตรวจจับการชนด้วยเลเซอร์

*เนื้อหานี้แปลโดยใช้ AI (เวอร์ชัน Beta) และอาจมีข้อผิดพลาด หากต้องการดูหน้านี้เป็นภาษาอังกฤษ ให้คลิกที่นี่

ในบทเรียนนี้ คุณจะได้เรียนรู้วิธีการยิงเลเซอร์จากบลาสเตอร์ใน สร้างเครื่องมือสำหรับผู้เล่น และตรวจสอบว่าเลเซอร์ชนกับผู้เล่นหรือไม่

การเรย์แคสเพื่อตรวจหาการชน

การเรย์แคส สร้างรังสีที่มองไม่เห็นจากตำแหน่งเริ่มต้นไปยังทิศทางที่กำหนดด้วยความยาวที่กำหนด หากรังสีชนกับวัตถุหรือภูมิประเทศในเส้นทาง มันจะส่งคืนข้อมูลเกี่ยวกับการชน เช่น ตำแหน่งและวัตถุที่ชน

เรย์แคสจาก A ไปยัง B ชนกับกำแพง

ค้นหาตำแหน่งเมาส์

ก่อนที่เลเซอร์จะถูกยิง คุณต้องรู้ก่อนว่าผู้เล่นกำลังเล็งไปที่ไหน สิ่งนี้สามารถค้นหาได้โดยการเรย์แคสจากตำแหน่งเมาส์ 2D ของผู้เล่นบนหน้าจอไปข้างหน้าโดยตรงจากกล้องเข้าสู่โลก 3D รังสีจะชนกับสิ่งที่ผู้เล่นเล็งด้วยเมาส์

  1. เปิดสคริปต์ ToolController ภายในเครื่องมือบลาสเตอร์จาก สร้างเครื่องมือสำหรับผู้เล่น หากคุณยังไม่ได้ทำบทเรียนนี้ คุณสามารถดาวน์โหลดโมเดล Blaster และแทรกลงใน StarterPack

  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. ใช้ฟังก์ชัน GetMouseLocation ของ UserInputService เพื่อรับตำแหน่งเมาส์ 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 เป็นที่รู้จักแล้ว คุณสามารถใช้คุณสมบัติ X และ Y เป็นพารามิเตอร์สำหรับฟังก์ชัน Camera:ViewportPointToRay() ซึ่งสร้าง Ray จากหน้าจอเข้าสู่โลก 3D

  1. ใช้คุณสมบัติ X และ Y ของ mouseLocation เป็นอาร์กิวเมนต์สำหรับฟังก์ชัน ViewportPointToRay() กำหนดให้กับตัวแปรชื่อ screenToWorldRay

    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- สร้างรังสีจากตำแหน่งเมาส์ 2D
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    end

ถึงเวลาที่จะใช้ฟังก์ชัน Raycast เพื่อตรวจสอบว่ารังสีชนกับวัตถุหรือไม่ สิ่งนี้ต้องการตำแหน่งเริ่มต้นและเวกเตอร์ทิศทาง: ในตัวอย่างนี้ คุณจะใช้คุณสมบัติ origin และ direction ของ 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 ของ workspace โดยส่งคุณสมบัติ Origin ของ 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 ซึ่งมีข้อมูลเกี่ยวกับการชนระหว่างรังสีและวัตถุ

คุณสมบัติ RaycastResultคำอธิบาย
Instanceเซลล์ BasePart หรือ Terrain ที่รังสีตัดผ่าน
Positionตำแหน่งที่เกิดการตัดกัน; โดยปกติจะเป็นจุดที่อยู่บนพื้นผิวของส่วนหรือภูมิประเทศ
Materialวัสดุที่จุดตัดกัน
Normalเวกเตอร์ปกติของด้านที่ตัดกัน สิ่งนี้สามารถใช้เพื่อกำหนดทิศทางที่ด้านนั้นชี้

คุณสมบัติ Position จะเป็นตำแหน่งของวัตถุที่เมาส์อยู่เหนือ หากเมาส์ไม่ได้อยู่เหนือวัตถุใด ๆ ภายในระยะ MAX_MOUSE_DISTANCE raycastResult จะเป็น nil

  1. สร้างคำสั่ง if เพื่อตรวจสอบว่า raycastResult มีอยู่หรือไม่

  2. หาก raycastResult มีค่า ให้ส่งคืนคุณสมบัติ Position ของมัน

  3. หาก raycastResult เป็น nil ให้ค้นหาสิ้นสุดของการเรย์แคส คำนวณตำแหน่ง 3D ของเมาส์โดยการบวก screenToWorldRay.Origin และ directionVector เข้าด้วยกัน

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 เป็นที่รู้จักแล้ว สามารถใช้เป็น ตำแหน่งเป้าหมาย เพื่อยิงเลเซอร์ไปยังเป้าหมายได้ รังสีที่สองสามารถถูกยิงระหว่างตำแหน่งอาวุธของผู้เล่นและตำแหน่งเป้าหมายโดยใช้ฟังก์ชัน Raycast

  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. สร้างฟังก์ชันชื่อ fireWeapon ใต้ฟังก์ชัน 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 โดยใช้คุณสมบัติ Unit ของมัน ซึ่งทำให้มันง่ายต่อการคูณด้วยความยาวในภายหลัง

    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    -- คำนวณเวกเตอร์ทิศทางที่มีขนาดปกติและคูณด้วยระยะเลเซอร์
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. ประกาศตัวแปรชื่อ directionVector และกำหนดให้เป็น targetDirection คูณด้วย MAX_LASER_DISTANCE

    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    -- ทิศทางในการยิงอาวุธ คูณด้วยระยะทางสูงสุด
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

วัตถุ RaycastParams สามารถใช้เพื่อเก็บพารามิเตอร์เพิ่มเติมสำหรับฟังก์ชันเรย์แคส มันจะถูกใช้ในบลาสเตอร์เลเซอร์ของคุณเพื่อให้แน่ใจว่าการเรย์แคสไม่ชนกับผู้เล่นที่ยิงอาวุธ วัตถุใด ๆ ที่รวมอยู่ในคุณสมบัติ FilterDescendantsInstances ของอ็อบเจ็กต์ RaycastParams จะถูก ละเว้น ในการเรย์แคส

  1. ต่อฟังก์ชัน fireWeapon และประกาศตัวแปรชื่อ weaponRaycastParams กำหนดให้เป็นอ็อบเจ็กต์ RaycastParams ใหม่

  2. สร้างตารางที่มี character ของผู้เล่นในท้องถิ่นและกำหนดให้กับคุณสมบัติ 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. ใช้คำสั่ง if เพื่อตรวจสอบว่า weaponRaycastResult มีค่า หากวัตถุถูกชน ให้กำหนด weaponRaycastResult.Position ให้กับ hitPosition

    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- ตรวจสอบว่าวัตถุใด ๆ ถูกชนระหว่างตำแหน่งเริ่มต้นและตำแหน่งสุดท้าย
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. หาก weaponRaycastResult ไม่มีค่า ให้คำนวณตำแหน่งสุดท้ายของการเรย์แคสโดยการบวกตำแหน่งของมือเครื่องมือกับ directionVector กำหนดให้กับ hitPosition

    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. เพิ่มโค้ดที่เน้นไว้ด้านล่างไปยังคำสั่ง if ของ 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("ผู้เล่นถูกชน")
    end
    end
    else
    -- คำนวณตำแหน่งสุดท้ายตามระยะเลเซอร์สูงสุด
    hitPosition = tool.Handle.Position + directionVector
    end

ตอนนี้บลาสเตอร์เลเซอร์ควรพิมพ์ ผู้เล่นถูกชน ไปยังหน้าต่าง Output ทุกครั้งที่การดำเนินการเรย์แคสชนกับผู้เล่นคนอื่น

ทดสอบกับผู้เล่นหลายคน

ต้องการผู้เล่นสองคนเพื่อตรวจสอบว่าเรย์แคสอาวุธพบผู้เล่นคนอื่นหรือไม่ ดังนั้นคุณต้องเริ่มเซิร์ฟเวอร์ท้องถิ่น ดูที่ การจำลองหลายคลายเอนต์ สำหรับคำแนะนำ

ในหนึ่งไคลเอนต์ ทดสอบการยิงผู้เล่นอีกคนด้วยอาวุธโดยการคลิกที่พวกเขา "ผู้เล่นถูกชน" ควรแสดงในผลลัพธ์ทุกครั้งที่ผู้เล่นถูกยิง

ค้นหาตำแหน่งเลเซอร์

บลาสเตอร์ควรยิงลำแสงสีแดงไปยังเป้าหมาย ฟังก์ชันสำหรับสิ่งนี้จะอยู่ภายใน ModuleScript เพื่อให้สามารถนำกลับมาใช้ใหม่ในสคริปต์อื่น ๆ ได้ในภายหลัง ก่อนอื่น สคริปต์จะต้องค้นหาตำแหน่งที่ลำแสงเลเซอร์ควรจะถูกเรนเดอร์

  1. สร้าง ModuleScript ชื่อ LaserRenderer ซึ่งเป็นบิดาไปยัง StarterPlayerScripts ภายใต้ StarterPlayer

  2. เปิดสคริปต์และเปลี่ยนชื่อของตารางโมดูลเป็นชื่อของสคริปต์ LaserRenderer

  3. ประกาศตัวแปรชื่อ SHOT_DURATION โดยมีค่าเป็น 0.15 ซึ่งจะเป็นระยะเวลาที่เลเซอร์มองเห็นได้ (เป็นวินาที)

  4. สร้างฟังก์ชันของ LaserRenderer ชื่อ createLaser โดยมีพารามิเตอร์สองตัวคือ toolHandle และ endPosition

    local LaserRenderer = {}
    local SHOT_DURATION = 0.15 -- เวลาที่เลเซอร์มองเห็นได้
    -- สร้างลำแสงเลเซอร์จากตำแหน่งเริ่มต้นไปยังตำแหน่งสุดท้าย
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. ประกาศตัวแปรชื่อ startPosition และตั้งค่าคุณสมบัติ Position ของ toolHandle เป็นค่า นี่จะเป็นตำแหน่งของบลาสเตอร์เลเซอร์ของผู้เล่น

  6. ประกาศตัวแปรชื่อ laserDistance และลบ endPosition ออกจาก startPosition เพื่อหาความแตกต่างระหว่างเวกเตอร์ทั้งสอง ใช้คุณสมบัติ Magnitude ของสิ่งนี้เพื่อหาความยาวของลำแสงเลเซอร์

    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

สร้างชิ้นส่วนเลเซอร์

ตอนนี้คุณรู้แล้วว่าจะสร้างลำแสงเลเซอร์ได้ที่ไหน คุณต้องเพิ่มลำแสงเอง สิ่งนี้สามารถทำได้ง่าย ๆ ด้วยชิ้นส่วน Neon

  1. ประกาศตัวแปร laserPart และกำหนดให้เป็นอ็อบเจ็กต์ Part ใหม่

  2. ตั้งค่าคุณสมบัติของ laserPart ดังต่อไปนี้:

    1. Size: Vector3.new(0.2, 0.2, laserDistance)
    2. CFrame: laserCFrame
    3. Anchored: true
    4. CanCollide: false
    5. Color: Color3.fromRGB(225, 0, 0) (สีแดงเข้ม)
    6. Material: Enum.Material.Neon
  3. พ่อแม่ laserPart ไปยัง Workspace

  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 เพื่อให้ถูกลบและทำความสะอาด
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

ตอนนี้ฟังก์ชันในการเรนเดอร์ลำแสงเลเซอร์เสร็จสมบูรณ์แล้ว สามารถเรียกใช้ได้จาก ToolController

  1. ที่ด้านบนของสคริปต์ ToolController ประกาศตัวแปรชื่อ LaserRenderer และเรียกใช้โมดูล LaserRenderer ที่ตั้งอยู่ใน PlayerScripts

    local UserInputService = game:GetService("UserInputService")
    local Players = game:GetService("Players")
    local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
    local tool = script.Parent
  2. ที่ด้านล่างของฟังก์ชัน fireWeapon เรียกใช้ฟังก์ชัน LaserRenderer createLaser โดยใช้มือเครื่องมือและ hitPosition เป็นอาร์กิวเมนต์

    -- คำนวณตำแหน่งสุดท้ายตามระยะเลเซอร์สูงสุด
    hitPosition = tool.Handle.Position + directionVector
    end
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  3. ทดสอบอาวุธโดยคลิกที่ปุ่ม Play ลำแสงเลเซอร์ควรปรากฏระหว่างอาวุธและเมาส์เมื่อเครื่องมือถูกเปิดใช้งาน

ควบคุมอัตราการยิงของอาวุธ

อาวุธต้องมีการหน่วงเวลาระหว่างการยิงแต่ละครั้งเพื่อหยุดผู้เล่นจากการทำความเสียหายมากเกินไปในระยะเวลาสั้น ๆ สิ่งนี้สามารถควบคุมได้โดยการตรวจสอบว่ามีเวลาผ่านไปเพียงพอจากการยิงครั้งล่าสุดของผู้เล่นหรือไม่

  1. ประกาศตัวแปรที่ด้านบนของ ToolController ชื่อ 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 โดยไม่มีพารามิเตอร์ ฟังก์ชันนี้จะดูว่ามีเวลาผ่านไปตั้งแต่การยิงครั้งก่อนและส่งคืน true หรือ false

    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
    -- ตรวจสอบว่ามีเวลาผ่านไปเพียงพอจากการยิงครั้งก่อนหรือไม่
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. ภายในฟังก์ชัน ประกาศตัวแปรชื่อ currentTime กำหนดให้เป็นผลลัพธ์ของการเรียกใช้ฟังก์ชัน tick() ซึ่งส่งคืนเวลาที่ผ่านไปในวินาทีตั้งแต่วันที่ 1 มกราคม 1970 (วันที่อ้างอิงที่ใช้กันทั่วไปในการคำนวณเวลา)

  5. ลบ timeOfPreviousShot ออกจาก currentTime และส่งคืน false หากผลลัพธ์น้อยกว่า FIRE_RATE มิฉะนั้นส่งคืน true

    -- ตรวจสอบว่ามีเวลาผ่านไปเพียงพอจากการยิงครั้งก่อนหรือไม่
    local function canShootWeapon()
    local currentTime = tick()
    if currentTime - timeOfPreviousShot < FIRE_RATE then
    return false
    end
    return true
    end
  6. ที่ท้ายฟังก์ชัน fireWeapon อัปเดต timeOfPreviousShot ทุกครั้งที่อาวุธถูกยิงโดยใช้ tick

    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. ภายในฟังก์ชัน toolActivated สร้างคำสั่ง if และเรียกใช้ canShootWeapon เพื่อตรวจสอบว่าอาวุธสามารถยิงได้หรือไม่

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

เมื่อคุณทดสอบบลาสเตอร์ คุณควรพบว่าไม่ว่าคุณจะคลิกเร็วแค่ไหน จะมีการหน่วงเวลา 0.3 วินาทีระหว่างการยิงแต่ละครั้งเสมอ

ทำให้ผู้เล่นได้รับความเสียหาย

ไคลเอนต์ไม่สามารถทำให้ไคลเอนต์อื่นได้รับความเสียหายโดยตรง เซิร์ฟเวอร์ต้องรับผิดชอบในการออกความเสียหายเมื่อผู้เล่นถูกชน

ไคลเอนต์สามารถใช้ RemoteEvent เพื่อบอกเซิร์ฟเวอร์ว่าตัวละครถูกชน เหตุการณ์เหล่านี้ควรเก็บไว้ใน ReplicatedStorage ซึ่งสามารถมองเห็นได้ทั้งไคลเอนต์และเซิร์ฟเวอร์

  1. สร้าง Folder ใน ReplicatedStorage ชื่อ Events

  2. แทรก RemoteEvent ลงในโฟลเดอร์ Events และตั้งชื่อว่า DamageCharacter

  3. ใน ToolController สร้างตัวแปรที่จุดเริ่มต้นของสคริปต์สำหรับ ReplicatedStorage และโฟลเดอร์ Events

    local UserInputService = game:GetService("UserInputService")
    local Players = game:GetService("Players")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
    local tool = script.Parent
    local eventsFolder = ReplicatedStorage.Events
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  4. แทนที่คำสั่งพิมพ์ "ผู้เล่นถูกชน" ใน fireWeapon ด้วยบรรทัดของ Luau เพื่อยิงเหตุการณ์ DamageCharacter โดยใช้ตัวแปร characterModel เป็นอาร์กิวเมนต์

    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

เซิร์ฟเวอร์ต้องจัดการความเสียหายให้กับผู้เล่นที่ถูกชนเมื่อเหตุการณ์ถูกยิง

  1. แทรก Script ลงใน ServerScriptService และตั้งชื่อว่า ServerLaserManager

  2. ประกาศตัวแปรชื่อ LASER_DAMAGE และตั้งค่าเป็น 10 หรือค่าที่คุณเลือก

  3. สร้างฟังก์ชันชื่อ damageCharacter โดยมีพารามิเตอร์สองตัวคือ playerFired และ characterToDamage

  4. ภายในฟังก์ชัน ค้นหามนุษย์ของตัวละครและลบ LASER_DAMAGE ออกจากสุขภาพของมัน

  5. เชื่อมต่อฟังก์ชัน damageCharacter กับเหตุการณ์ DamageCharacter ในโฟลเดอร์ Events

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    function damageCharacter(playerFired, characterToDamage)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- ลบสุขภาพจากตัวละคร
    humanoid.Health -= LASER_DAMAGE
    end
    end
    -- เชื่อมต่อเหตุการณ์กับฟังก์ชันที่เหมาะสม
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. ทดสอบบลาสเตอร์กับผู้เล่น 2 คนโดยเริ่มเซิร์ฟเวอร์ท้องถิ่น เมื่อคุณยิงผู้เล่นอีกคน สุขภาพของพวกเขาจะลดลงตามค่าที่กำหนดใน LASER_DAMAGE

เรนเดอร์ลำแสงเลเซอร์ของผู้เล่นคนอื่น

ปัจจุบันลำแสงเลเซอร์ถูกสร้างโดยไคลเอนต์ที่ยิงอาวุธ ดังนั้นเฉพาะพวกเขาที่จะสามารถมองเห็นลำแสงเลเซอร์ได้

หากลำแสงเลเซอร์ถูกสร้างขึ้นบนเซิร์ฟเวอร์ ทุกคนจะสามารถมองเห็นมันได้ อย่างไรก็ตาม จะมี ความล่าช้า เล็กน้อยระหว่างการยิงอาวุธของไคลเอนต์และเซิร์ฟเวอร์ที่ได้รับข้อมูลเกี่ยวกับการยิง สิ่งนี้หมายความว่าไคลเอนต์ที่ยิงอาวุธจะเห็นความล่าชาระหว่างเมื่อพวกเขาเปิดใช้งานอาวุธและเมื่อพวกเขาเห็นลำแสงเลเซอร์ อาวุธจะรู้สึกช้าเป็นผล

เพื่อแก้ไขปัญหานี้ ไคลเอนต์แต่ละคนจะสร้างลำแสงเลเซอร์ของตนเอง ซึ่งหมายความว่าไคลเอนต์ที่ยิงอาวุธจะเห็นลำแสงเลเซอร์ทันที ไคลเอนต์อื่น ๆ จะประสบกับความล่าช้าเล็กน้อยระหว่างเมื่อผู้เล่นคนอื่นยิงและลำแสงปรากฏ นี่คือสถานการณ์ที่ดีที่สุด: ไม่มีวิธีการสื่อสารลำแสงเลเซอร์ของไคลเอนต์หนึ่งไปยังไคลเอนต์อื่นได้เร็วขึ้น

ไคลเอนต์ที่ยิง

ก่อนอื่น ไคลเอนต์ต้องบอกเซิร์ฟเวอร์ว่ามันได้ยิงเลเซอร์และให้ตำแหน่งสุดท้าย

  1. แทรก RemoteEvent ลงในโฟลเดอร์ Events ใน ReplicatedStorage และตั้งชื่อว่า LaserFired

  2. ค้นหาฟังก์ชัน fireWeapon ในสคริปต์ ToolController ที่ท้ายฟังก์ชัน ให้ยิงเหตุการณ์ LaserFired โดยใช้ hitPosition เป็นอาร์กิวเมนต์

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

เซิร์ฟเวอร์

เซิร์ฟเวอร์ต้องรับเหตุการณ์ที่ไคลเอนต์ยิงและบอกไคลเอนต์ทั้งหมดเกี่ยวกับตำแหน่งเริ่มต้นและตำแหน่งสุดท้ายของลำแสงเลเซอร์เพื่อให้พวกเขาสามารถเรนเดอร์ได้เช่นกัน

  1. ในสคริปต์ ServerLaserManager สร้างฟังก์ชันชื่อ playerFiredLaser ข้างบน damageCharacter โดยมีพารามิเตอร์สองตัวคือ playerFired และ endPosition

  2. เชื่อมต่อฟังก์ชันกับเหตุการณ์ LaserFired

    -- แจ้งให้ไคลเอนต์ทั้งหมดทราบว่ามีการยิงเลเซอร์เพื่อให้พวกเขาสามารถแสดงลำแสงได้
    local function playerFiredLaser(playerFired, endPosition)
    end
    -- เชื่อมต่อเหตุการณ์กับฟังก์ชันที่เหมาะสม
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
    eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

เซิร์ฟเวอร์ต้องการตำแหน่งเริ่มต้นของลำแสง สิ่งนี้สามารถส่งจากไคลเอนต์ได้ แต่ดีที่สุดคือหลีกเลี่ยงการเชื่อใจไคลเอนต์เมื่อเป็นไปได้ ตำแหน่งมือของอาวุธของตัวละครจะเป็นตำแหน่งเริ่มต้น ดังนั้นเซิร์ฟเวอร์สามารถค้นหามันจากที่นั่น

  1. สร้างฟังก์ชัน getPlayerToolHandle ข้างบนฟังก์ชัน playerFiredLaser โดยมีพารามิเตอร์ชื่อ player

  2. ใช้โค้ดต่อไปนี้เพื่อค้นหาตัวละครของผู้เล่นสำหรับอาวุธและส่งคืนวัตถุ handle

    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)

เซิร์ฟเวอร์สามารถเรียกใช้ FireAllClients บนเหตุการณ์ LaserFired เพื่อส่งข้อมูลที่จำเป็นในการเรนเดอร์ลำแสงไปยังไคลเอนต์ ซึ่งรวมถึง ผู้เล่น ที่ยิงลำแสง (เพื่อให้ไคลเอนต์สำหรับผู้เล่นนั้นไม่เรนเดอร์ลำแสงสองครั้ง) handle ของบลาสเตอร์ (ซึ่งทำหน้าที่เป็นตำแหน่งเริ่มต้นสำหรับลำแสง) และตำแหน่งสุดท้ายของลำแสง

  1. ในฟังก์ชัน playerFiredLaser ให้เรียกใช้ฟังก์ชัน getPlayerToolHandle โดยใช้ playerFired เป็นอาร์กิวเมนต์และกำหนดค่าให้กับตัวแปรชื่อ toolHandle

  2. หาก toolHandle มีอยู่ ให้ยิงเหตุการณ์ LaserFired สำหรับไคลเอนต์ทั้งหมดโดยใช้ playerFired, toolHandle และ endPosition เป็นอาร์กิวเมนต์

    -- แจ้งให้ไคลเอนต์ทั้งหมดทราบว่ามีการยิงเลเซอร์เพื่อให้พวกเขาสามารถแสดงลำแสงได้
    local function playerFiredLaser(playerFired, endPosition)
    local toolHandle = getPlayerToolHandle(playerFired)
    if toolHandle then
    eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
    end
    end

เรนเดอร์บนไคลเอนต์

ตอนนี้ FireAllClients ถูกเรียกใช้ ไคลเอนต์แต่ละคนจะได้รับเหตุการณ์จากเซิร์ฟเวอร์เพื่อเรนเดอร์ลำแสงเลเซอร์ ไคลเอนต์แต่ละคนสามารถนำโมดูล LaserRenderer ที่ใช้ซ้ำได้จากก่อนหน้านี้มาใช้เพื่อเรนเดอร์ลำแสงเลเซอร์โดยใช้ตำแหน่งมือของเครื่องมือและค่าตำแหน่งสุดท้ายที่ส่งโดยเซิร์ฟเวอร์ ผู้เล่นที่ยิงลำแสงเลเซอร์ในครั้งแรกควรละเว้นเหตุการณ์นี้ มิฉะนั้นพวกเขาจะเห็นลำแสงสองลำ

  1. สร้าง LocalScript ใน StarterPlayerScripts ชื่อ ClientLaserManager

  2. ภายในสคริปต์ ให้เรียกใช้โมดูล LaserRenderer

  3. สร้างฟังก์ชันชื่อ createPlayerLaser โดยมีพารามิเตอร์ playerWhoShot, toolHandle และ endPosition

  4. เชื่อมต่อฟังก์ชันกับเหตุการณ์ LaserFired ในโฟลเดอร์ Events

  5. ในฟังก์ชัน ใช้คำสั่ง if เพื่อตรวจสอบว่า playerWhoShot ไม่เท่ากับ LocalPlayer

  6. ภายในคำสั่ง if ให้เรียกใช้ฟังก์ชัน createLaser จากโมดูล LaserRenderer โดยใช้ toolHandle และ endPosition เป็นอาร์กิวเมนต์

    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. ในสคริปต์ ToolController ไปที่ฟังก์ชัน toolActivated และลบบรรทัดที่เล่นเสียง Activate

    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. ที่ด้านล่างของฟังก์ชัน createLaser ใน LaserRenderer ประกาศตัวแปรชื่อ shootingSound และใช้วิธีการ FindFirstChild() ของ toolHandle เพื่อตรวจสอบเสียง Activate

  3. ใช้คำสั่ง if เพื่อตรวจสอบว่า shootingSound มีอยู่หรือไม่ หากมี ให้เรียกใช้ฟังก์ชัน Play()

    laserPart.Parent = workspace
    -- เพิ่มลำแสงเลเซอร์ไปยังบริการ Debris เพื่อให้ถูกลบและทำความสะอาด
    Debris:AddItem(laserPart, SHOT_DURATION)
    -- เล่นเสียงยิงของอาวุธ
    local shootingSound = toolHandle:FindFirstChild("Activate")
    if shootingSound then
    shootingSound:Play()
    end
    end

ป้องกัน remotes โดยใช้การตรวจสอบ

หากเซิร์ฟเวอร์ไม่ตรวจสอบข้อมูลจากคำขอที่เข้ามา แฮกเกอร์สามารถใช้ฟังก์ชันและเหตุการณ์ระยะไกลเพื่อส่งค่าปลอมไปยังเซิร์ฟเวอร์ได้ เป็นสิ่งสำคัญที่จะต้องใช้ การตรวจสอบด้านเซิร์ฟเวอร์ เพื่อป้องกันสิ่งนี้

ในรูปแบบปัจจุบัน เหตุการณ์ DamageCharacter มีความเสี่ยงต่อการโจมตีมาก แฮกเกอร์สามารถใช้เหตุการณ์นี้เพื่อทำให้ผู้เล่นคนใดก็ได้ได้รับความเสียหายโดยไม่ต้องยิงพวกเขา

การตรวจสอบคือกระบวนการตรวจสอบว่าค่าที่ส่งไปยังเซิร์ฟเวอร์นั้นสมจริง ในกรณีนี้ เซิร์ฟเวอร์จะต้อง:

  • ตรวจสอบว่าระยะห่างระหว่างผู้เล่นและตำแหน่งที่ถูกชนโดยเลเซอร์อยู่ภายในขอบเขตที่กำหนด
  • เรย์แคสระหว่างอาวุธที่ยิงเลเซอร์และตำแหน่งที่ถูกชนเพื่อให้แน่ใจว่าการยิงเป็นไปได้และไม่ได้ผ่านกำแพง

ไคลเอนต์

ไคลเอนต์ต้องส่งตำแหน่งที่ถูกชนโดยเรย์แคสไปยังเซิร์ฟเวอร์เพื่อให้สามารถตรวจสอบระยะทางได้ว่าสมจริง

  1. ใน ToolController ไปที่บรรทัดที่เหตุการณ์ DamageCharacter ถูกยิงในฟังก์ชัน fireWeapon

  2. เพิ่ม hitPosition เป็นอาร์กิวเมนต์

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

เซิร์ฟเวอร์

ไคลเอนต์กำลังส่งพารามิเตอร์เพิ่มเติมผ่านเหตุการณ์ DamageCharacter ดังนั้น ServerLaserManager ต้องปรับให้เข้ากับมัน

  1. ในสคริปต์ ServerLaserManager ให้เพิ่มพารามิเตอร์ hitPosition ในฟังก์ชัน damageCharacter

    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- ลบสุขภาพจากตัวละคร
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. ด้านล่างฟังก์ชัน getPlayerToolHandle สร้างฟังก์ชันชื่อ isHitValid โดยมีพารามิเตอร์สามตัว: playerFired, characterToDamage และ hitPosition

    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 ให้ส่งคืน false

    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 โดยมีสามอาร์กิวเมนต์: playerFired, characterToDamage และ hitPosition

  3. ในคำสั่ง if ด้านล่าง เพิ่มตัวดำเนินการ and เพื่อตรวจสอบว่า 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

ตอนนี้เหตุการณ์ damageCharacter มีความปลอดภัยมากขึ้นและจะป้องกันผู้เล่นส่วนใหญ่จากการใช้ประโยชน์จากมัน โปรดทราบว่าผู้เล่นที่มีเจตนาร้ายบางคนมักจะหาวิธีหลีกเลี่ยงการตรวจสอบ การรักษาความปลอดภัยของเหตุการณ์ระยะไกลเป็นความพยายามที่ต่อเนื่อง

บลาสเตอร์เลเซอร์ของคุณเสร็จสมบูรณ์แล้ว โดยมีระบบตรวจจับการชนพื้นฐานโดยใช้การเรย์แคส ลองดูที่ การตรวจจับการป้อนข้อมูลของผู้ใช้ เพื่อค้นหาวิธีที่คุณสามารถเพิ่มการกระทำการโหลดใหม่ให้กับบลาสเตอร์เลเซอร์ของคุณ หรือสร้างแผนที่สนุก ๆ และลองบลาสเตอร์เลเซอร์ของคุณกับผู้เล่นคนอื่น!

โค้ดสุดท้าย

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
-- ตรวจสอบว่ามีเวลาผ่านไปเพียงพอจากการยิงครั้งก่อนหรือไม่
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
-- เรย์แคสจากต้นกำเนิดของรังสีไปยังทิศทางของมัน
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)

LaserRenderer

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 เพื่อให้ถูกลบและทำความสะอาด
Debris:AddItem(laserPart, SHOT_DURATION)
-- เล่นเสียงยิงของอาวุธ
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer

ServerLaserManager

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- ค้นหามือของเครื่องมือที่ผู้เล่นถืออยู่
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)

ClientLaserManager

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)
©2026 Roblox Corporation. Roblox, โลโก้ Roblox และ Powering Imagination เป็นส่วนหนึ่งของเครื่องหมายการค้าที่จดทะเบียน และไม่ได้จดทะเบียนของเราในสหรัฐฯ และประเทศอื่นๆ