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

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

ที่ด้านบนของสคริปต์ ให้ประกาศค่าคงที่ชื่อ MAX_MOUSE_DISTANCE ด้วยค่า 1000
สร้างฟังก์ชันชื่อ getWorldMousePosition.
local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()endlocal function toolActivated()tool.Handle.Activate:Play()end-- เชื่อมต่อเหตุการณ์กับฟังก์ชันที่เหมาะสมtool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)ใช้ฟังก์ชัน GetMouseLocation ของ UserInputService เพื่อรับตำแหน่งเมาส์ 2D ของผู้เล่นบนหน้าจอ มอบหมายสิ่งนี้ให้กับตัวแปรชื่อ mouseLocation.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
ตอนนี้ตำแหน่งเมาส์ 2D เป็นที่รู้จักอยู่แล้ว คุณสามารถใช้คุณสมบัติ X และ Y เป็นพารามิเตอร์สำหรับฟังก์ชัน Camera:ViewportPointToRay() ซึ่งจะสร้าง Ray จากหน้าจอไปยังโลก 3D
ใช้คุณสมบัติ X และ Y ของ mouseLocation เป็นอาร์กิวเมนต์สำหรับฟังก์ชัน ViewportPointToRay() มอบหมายสิ่งนี้ให้กับตัวแปรชื่อ screenToWorldRay.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- สร้างเรย์จากตำแหน่งเมาส์ 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
ถึงเวลาใช้ฟังก์ชัน Raycast เพื่อตรวจสอบว่ารังสีชนกับวัตถุหรือไม่ สิ่งนี้ต้องการตำแหน่งเริ่มต้นและเวกเตอร์ทิศทาง: ในตัวอย่างนี้ คุณจะใช้คุณสมบัติ origin และ direction ของ screenToWorldRay.
ความยาวของเวกเตอร์ทิศทางกำหนดว่ารังสีจะเดินทางไปได้ไกลแค่ไหน รังสีจำเป็นต้องยาวพอ ๆ กับ MAX_MOUSE_DISTANCE ดังนั้นคุณจะต้องคูณเวกเตอร์ทิศทางด้วย MAX_MOUSE_DISTANCE.
ประกาศตัวแปรชื่อ directionVector และมอบหมายให้กับค่าของ screenToWorldRay.Direction คูณด้วย MAX_MOUSE_DISTANCE.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- สร้างเรย์จากตำแหน่งเมาส์ 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- เวกเตอร์ทิศทางหน่วยของเรย์คูณด้วยระยะสูงสุดlocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEเรียกใช้ฟังก์ชัน Raycast ของ workspace โดยส่งค่าคุณสมบัติ Origin ของ screenToWorldRay เป็นอาร์กิวเมนต์แรกและ directionVector เป็นอาร์กิวเมนต์ที่สอง มอบหมายสิ่งนี้ให้กับตัวแปรชื่อ raycastResult.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- สร้างเรย์จากตำแหน่งเมาส์ 2Dlocal 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.
สร้าง if statement เพื่อตรวจสอบว่า raycastResult มีอยู่หรือไม่
หาก raycastResult มีค่าให้ส่งคืนค่าคุณสมบัติ Position
หาก 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.
ประกาศค่าคงที่ชื่อ MAX_LASER_DISTANCE ที่ด้านบนของสคริปต์และกำหนดค่าเป็น 500 หรือระยะที่คุณเลือกสำหรับเลเซอร์บลาสเตอร์
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500สร้างฟังก์ชันชื่อ fireWeapon ภายใต้ฟังก์ชัน getWorldMousePosition.
เรียก getWorldMousePosition และมอบหมายผลลัพธ์ให้กับตัวแปรชื่อ mousePosition. นี่จะเป็นตำแหน่งเป้าหมายสำหรับการเรย์แคสต์.
-- ไม่พบวัตถุที่ชน ดังนั้นคำนวณตำแหน่งที่ปลายสุดของเรย์return screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
คราวนี้ เวกเตอร์ทิศทางสำหรับฟังก์ชันเรย์แคสต์จะแสดงถึงทิศทางจากตำแหน่งเครื่องมือของผู้เล่นไปยังตำแหน่งเป้าหมาย
ประกาศตัวแปรชื่อ targetDirection และคำนวณเวกเตอร์ทิศทางโดยการลบตำแหน่งเครื่องมือจาก mouseLocation.
ปรับปกติเวกเตอร์โดยใช้คุณสมบัติ Unit ของมัน นี่จะทำให้มันมีขนาด 1 ซึ่งทำให้มันง่ายต่อการคูณด้วยความยาวในภายหลัง
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- คำนวณเวกเตอร์ทิศทางที่ปรับปกติ และคูณด้วยระยะเลเซอร์local targetDirection = (mouseLocation - tool.Handle.Position).Unitendประกาศตัวแปรชื่อ directionVector และมอบหมายให้มันเป็น targetDirection คูณด้วย MAX_LASER_DISTANCE.
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- เวกเตอร์ที่ยิงอาวุธ คูณด้วยระยะสูงสุดlocal directionVector = targetDirection * MAX_LASER_DISTANCEend
อ็อบเจ็กต์ RaycastParams สามารถใช้เพื่อเก็บพารามิเตอร์เพิ่มเติมสำหรับฟังก์ชันเรย์แคสต์ มันจะถูกใช้ในเลเซอร์บลาสเตอร์ของคุณเพื่อให้แน่ใจว่าการเรย์แคสต์จะไม่ชนกับผู้เล่นที่ยิงอาวุธ ส่วนใด ๆ ที่รวมอยู่ในคุณสมบัติ FilterDescendantsInstances ของอ็อบเจกต์ RaycastParams จะถูก มองข้าม ในการเรย์แคสต์.
ต่อฟังก์ชัน fireWeapon และประกาศตัวแปรชื่อ weaponRaycastParams มอบหมายให้เป็นอ็อบเจกต์ใหม่ RaycastParams.
สร้างตารางที่มี character ของผู้เล่นและมอบหมายให้กับคุณสมบัติ weaponRaycastParams.FilterDescendantsInstances.
แคสต์จากตำแหน่งจับของเครื่องมือของผู้เล่น ไปในทิศทางของ 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
สุดท้าย คุณจำเป็นต้องตรวจสอบว่าการดำเนินการเรย์แคสต์ส่งคืนค่า หากส่งคืนค่า วัตถุจะถูกชนโดยรังสีและเลเซอร์สามารถสร้างระหว่างอาวุธกับตำแหน่งที่ชน หากไม่มีอะไรส่งคืน ตำแหน่งสุดท้ายจำเป็นต้องถูกคำนวณเพื่อสร้างเลเซอร์.
ประกาศตัวแปรว่างชื่อ hitPosition.
ใช้ if statement เพื่อตรวจสอบว่า weaponRaycastResult มีค่า หากวัตถุได้ถูกชน ให้มอบหมาย weaponRaycastResult.Position ให้กับ hitPosition.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- ตรวจสอบว่าวัตถุใด ๆ ได้ถูกชนระหว่างตำแหน่งเริ่มต้นและสิ้นสุดlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionendหาก weaponRaycastResult ไม่มีค่าให้คำนวณตำแหน่งสุดท้ายของการเรย์แคสต์โดยการบวกตำแหน่งของจับอาวุธกับ directionVector. มอบหมายสิ่งนี้ให้กับ hitPosition.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- ตรวจสอบว่าวัตถุใด ๆ ได้ถูกชนระหว่างตำแหน่งเริ่มต้นและสิ้นสุดlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- คำนวณตำแหน่งสุดท้ายตามระยะเลเซอร์สูงสุดhitPosition = tool.Handle.Position + directionVectorendendไปที่ฟังก์ชัน toolActivated และเรียกฟังก์ชัน fireWeapon เพื่อให้เลเซอร์ยิงทุกครั้งที่เครื่องมือถูกใช้งาน.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
ตรวจสอบว่าวัตถุที่ถูกชน
เพื่อตรวจสอบว่าวัตถุที่ถูกเลเซอร์ชนเป็นส่วนหนึ่งของตัวละครของผู้เล่นหรือแค่ส่วนหนึ่งของภูมิทัศน์ คุณจำเป็นต้องมองหา Humanoid เนื่องจากทุกตัวละครจะมีหนึ่งตัว
ก่อนอื่นคุณต้องหาตัว โมเดลตัวละคร หากส่วนหนึ่งของตัวละครถูกชน คุณไม่สามารถสมมติว่าพ่อตัวของวัตถุที่ถูกชนจะเป็นตัวละคร เลเซอร์อาจถูกชนที่ชิ้นส่วนของร่างกาย อุปกรณ์เสริม หรือเครื่องมือ ซึ่งทั้งหมดนี้อยู่ในส่วนต่าง ๆ ของลำดับชั้นของตัวละคร

คุณสามารถใช้ FindFirstAncestorOfClass เพื่อตรวจหาบรรพบุรุษโมเดลตัวละครของวัตถุที่ถูกเลเซอร์ชน หากมีหนึ่งถูกรายการอยู่ หากคุณพบโมเดลและมีมัมมี่อยู่ในนั้น ในกรณีส่วนใหญ่คุณสามารถสมมติว่าเป็นตัวละครของผู้เล่น
เพิ่มโค้ดที่ทำเครื่องหมายไว้ด้านล่างลงใน if statement ของ weaponRaycastResult เพื่อตรวจสอบว่ามีตัวละครที่ถูกชนหรือไม่
-- ตรวจสอบว่าวัตถุใด ๆ ได้ถูกชนระหว่างตำแหน่งเริ่มต้นและสิ้นสุดlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- สกุลที่ถูกชนจะเป็นบุตรของโมเดลตัวละคร-- หากพบมัมมี่ในโมเดลนั้นก็อาจเป็นตัวละครของผู้เล่นlocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("ผู้เล่นที่ถูกชน")endendelse-- คำนวณตำแหน่งสุดท้ายตามระยะเลเซอร์สูงสุดhitPosition = tool.Handle.Position + directionVectorend
ตอนนี้เลเซอร์บลาสเตอร์ควรพิมพ์ ผู้เล่นที่ถูกชน ลงในหน้าต่าง Output ทุกครั้งที่การดำเนินการเรย์แคสต์ชนกับผู้เล่นคนอื่น
ทดสอบด้วยผู้เล่นหลายคน
ต้องการผู้เล่นสองคนเพื่อทดสอบว่าอาวุธเรย์แคสต์กำลังค้นหาผู้เล่นคนอื่นอยู่ ดังนั้นคุณจำเป็นต้องเริ่มเซิร์ฟเวอร์ท้องถิ่น ดูคำแนะนำที่ multi‑client simulation.
ในคลิปหนึ่ง ทดสอบการยิงผู้เล่นคนอื่นด้วยอาวุธโดยการคลิกที่พวกเขา "ผู้เล่นที่ถูกชน" ควรแสดงผลในเอาท์พุททุกครั้งที่ผู้เล่นถูกยิง.
หาโลเคชันของเลเซอร์
บลาสเตอร์ควรยิงลำแสงสีแดงไปที่ตำแหน่งเป้าหมาย ฟังก์ชันนี้จะอยู่ภายใน ModuleScript เพื่อให้สามารถใช้ซ้ำในสคริปต์อื่น ๆ ได้ในภายหลัง ก่อนอื่น สคริปต์จะต้องหาตำแหน่งที่ลำแสงเลเซอร์ควรถูกเรนเดอร์
สร้าง ModuleScript ชื่อ LaserRenderer ที่คุณเป็นพ่อแม่ให้กับ StarterPlayerScripts ภายใต้ StarterPlayer.

เปิดสคริปต์และเปลี่ยนชื่อฟังก์ชันของโมดูลเป็นชื่อของสคริปต์ LaserRenderer.
ประกาศตัวแปรชื่อ SHOT_DURATION โดยมีค่าของ 0.15 นี่จะเป็นระยะเวลาที่ (เป็นวินาที) เลเซอร์สามารถมองเห็นได้
สร้างฟังก์ชันของ LaserRenderer ชื่อ createLaser โดยมีพารามิเตอร์สองพารามิเตอร์ชื่อ toolHandle และ endPosition.
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- ระยะเวลาที่เลเซอร์เห็นได้-- สร้างลำแสงเลเซอร์จากตำแหน่งเริ่มต้นไปยังตำแหน่งสิ้นสุดfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererประกาศตัวแปรชื่อ startPosition และกำหนดคุณสมบัติ Position ของ toolHandle เป็นค่า นี่จะเป็นตำแหน่งของเลเซอร์บลาสเตอร์ของผู้เล่น
ประกาศตัวแปรชื่อ laserDistance และลบ endPosition จาก startPosition เพื่อตรวจหาความแตกต่างระหว่างสองเวกเตอร์ ใช้คุณสมบัติ Magnitude ของสิ่งนี้เพื่อให้ได้ความยาวของลำแสงเลเซอร์
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudeendประกาศตัวแปร laserCFrame เพื่อเก็บตำแหน่งและทิศทางของลำแสงเลเซอร์ ตำแหน่งต้องเป็นจุดกึ่งกลางของจุดเริ่มต้นและจุดสิ้นสุดของลำแสง ใช้ CFrame.lookAt เพื่อสร้าง CFrame ใหม่ที่ตั้งอยู่ที่ startPosition และมองไปทาง endPosition คูณกับ CFrame ใหม่ที่มีค่าแกน Z เป็นครึ่งหนึ่งของลบ laserDistance เพื่อให้ได้จุดกึ่งกลาง
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)end
สร้างส่วนประกอบเลเซอร์
ตอนนี้คุณรู้แล้วว่าต้องสร้างลำแสงเลเซอร์อย่างไร คุณจำเป็นต้องเพิ่มลำแสงเอง สิ่งนี้สามารถทำได้ง่าย ๆ กับ 'พาร์ท' สไตล์ Neon
ประกาศตัวแปร laserPart และกำหนดเป็นอ็อบเจ็กต์ Part ใหม่
ตั้งค่าคุณสมบัติต่อไปนี้ของ laserPart:
- Size: Vector3.new(0.2, 0.2, laserDistance)
- CFrame: laserCFrame
- Anchored: true
- CanCollide: false
- Color: Color3.fromRGB(225, 0, 0) (เป็นสีแดงจัด)
- Material: Enum.Material.Neon
เปิดเผย laserPart ไปที่ Workspace.
เพิ่มส่วนที่ไปยังบริการ Debris เพื่อให้มันถูกลบหลังจากระยะเวลาของวินาทีในตัวแปร SHOT_DURATION.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal 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 = laserCFramelaserPart.Anchored = truelaserPart.CanCollide = falselaserPart.Color = Color3.fromRGB(225, 0, 0)laserPart.Material = Enum.Material.NeonlaserPart.Parent = workspace-- เพิ่มลำแสงเลเซอร์ไปยังบริการ Debris เพื่อลบ & ทำความสะอาดDebris:AddItem(laserPart, SHOT_DURATION)end
ตอนนี้ฟังก์ชันเพื่อเรนเดอร์ลำแสงเลเซอร์เสร็จสมบูรณ์แล้ว คุณสามารถเรียกใช้งานจาก ToolController.
ที่ด้านบนของสคริปต์ 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ที่ด้านล่างของฟังก์ชัน fireWeapon, เรียกใช้ฟังก์ชัน createLaser ของ LaserRenderer โดยใช้ handle ของเครื่องมือและ hitPosition เป็นอาร์กิวเมนต์.
-- คำนวณตำแหน่งสุดท้ายตามระยะเลเซอร์สูงสุดhitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endทดสอบอาวุธโดยการคลิกที่ปุ่ม Play จะแสงเลเซอร์ควรจะแสดงหน้าอยู่ระหว่างอาวุธและเมาส์เมื่อเครื่องมือทำงาน.
ควบคุมอัตราการยิงของอาวุธ
อาวุธจำเป็นต้องมีการหน่วงระหว่างการยิงแต่ละครั้งเพื่อหยุดไม่ให้ผู้เล่นสร้างความเสียหายมากเกินไปในระยะเวลาอันสั้น สิ่งนี้สามารถควบคุมได้โดยการตรวจสอบว่ามีระยะเวลาผ่านไปตั้งแต่ผู้เล่นยิงครั้งล่าสุดหรือไม่
ประกาศตัวแปรที่ด้านบนของ ToolController ชื่อ FIRE_RATE นี่จะเป็นเวลาอย่างน้อยระหว่างการยิงแต่ละครั้ง กำหนดค่าให้กับมันตามที่คุณเลือก ตัวอย่างนี้ใช้ 0.3 วินาที
ประกาศตัวแปรอีกตัวด้านล่างชื่อ timeOfPreviousShot โดยมีค่าเป็น 0 นี้จะจัดเก็บเวลาสุดท้ายที่ผู้เล่นยิงและจะถูกปรับปรุงด้วยการยิงแต่ละครั้ง
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0สร้างฟังก์ชันชื่อ canShootWeapon โดยไม่มีพารามิเตอร์ ฟังก์ชันนี้จะมองว่ามีเวลาดำเนินการประกอบไปจากผู้ยิงครั้งก่อนหน้านี้หรือไม่และส่งคืน true หรือ false.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- ตรวจสอบว่ามีเวลาผ่านไปพอสมควรระหว่างการยิงครั้งก่อนlocal function canShootWeapon()endlocal function getWorldMousePosition()ภายในฟังก์ชันให้ประกาศตัวแปรชื่อ currentTime; มอบหมายให้ค่าที่ได้จากการเรียกใช้ฟังก์ชัน tick() ฟังก์ชันนี้จะส่งคืนว่ามีกี่วินาทีที่ใช้เวลาผ่านไปตั้งแต่วันที่ 1 มกราคม 1970 (วันที่เฉพาะอย่างเป็นที่รู้จักกันดีที่ใช้ในการคำนวณเวลา)
ลบ timeOfPreviousShot จาก currentTime และส่งคืน false หากผลลัพธ์มีขนาดเล็กกว่า FIRE_RATE; มิฉะนั้นส่งคืน true.
-- ตรวจสอบว่ามีเวลาผ่านไปพอสมควรระหว่างการยิงครั้งก่อนlocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendที่ด้านล่างของฟังก์ชัน fireWeapon, อัพเดท timeOfPreviousShot ทุกครั้งที่อาวุธถูกยิงโดยใช้ tick.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endภายในฟังก์ชัน toolActivated สร้าง if statement และเรียก canShootWeapon เพื่อตรวจสอบว่าอาวุธสามารถถูกยิงได้หรือไม่.
local function toolActivated()if canShootWeapon() thenfireWeapon()endend
เมื่อคุณทำการทดสอบบลาสเตอร์ คุณควรพบว่าไม่ว่าคุณจะคลิกเร็วแค่ไหน จะมีการหน่วงระยะเวลา 0.3 วินาทีระหว่างการยิงครั้งต่อไปเสมอ.
ทำร้ายผู้เล่น
ลูกค้าไม่สามารถทำร้ายลูกค้าโดยตรง เซิร์ฟเวอร์จำเป็นต้องรับผิดชอบในการออกความเสียหายเมื่อผู้เล่นถูกตี
ลูกค้าสามารถใช้ RemoteEvent เพื่อบอกเซิร์ฟเวอร์ว่าตัวละครถูกตี สิ่งนี้ควรเก็บไว้ใน ReplicatedStorage ซึ่งมองเห็นได้ทั้งลูกค้าและเซิร์ฟเวอร์
สร้าง Folder ใน ReplicatedStorage ชื่อ Events.

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

ใน 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.Parentlocal eventsFolder = ReplicatedStorage.Eventslocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500เปลี่ยนบรรทัดที่พิมพ์ "ผู้เล่นที่ถูกชน" ใน fireWeapon เป็นบรรทัดที่เรียกใช้เหตุการณ์ DamageCharacter โดยใช้ตัวแปร characterModel เป็นอาร์กิวเมนต์.
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endendelse-- คำนวณตำแหน่งสุดท้ายตามระยะเลเซอร์สูงสุดhitPosition = tool.Handle.Position + directionVectorend
เซิร์ฟเวอร์จะต้องทำความเสียหายให้ผู้เล่นที่ถูกตีเมื่อเหตุการณ์ถูกส่งออก
แทรก Script ลงใน ServerScriptService และตั้งชื่อว่า ServerLaserManager.

ประกาศตัวแปรชื่อ LASER_DAMAGE และตั้งค่าเป็น 10 หรือค่าที่คุณเลือก
สร้างฟังก์ชันชื่อ damageCharacter โดยมีพารามิเตอร์สองพารามิเตอร์ชื่อ playerFired และ characterToDamage.
ภายในฟังก์ชัน ให้หามัมมี่ของตัวละครและลบ LASER_DAMAGE ออกจากพลังงานของมัน
เชื่อมต่อฟังก์ชัน damageCharacter กับเหตุการณ์ DamageCharacter ในโฟลเดอร์ Events.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- ลดพลังงานของตัวละครhumanoid.Health -= LASER_DAMAGEendend-- เชื่อมต่อเหตุการณ์กับฟังก์ชันที่เหมาะสมeventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)ทดสอบบลาสเตอร์กับผู้เล่น 2 คนโดยเริ่มเซิร์ฟเวอร์ท้องถิ่น เมื่อคุณยิงผู้เล่นคนอื่น พลังงานของพวกเขาจะลดลงตามจำนวนที่กำหนดใน LASER_DAMAGE.
เรนเดอร์ลำแสงเลเซอร์ของผู้เล่นคนอื่น
ปัจจุบันลำแสงเลเซอร์ถูกสร้างโดยลูกค้าที่ยิงอาวุธ ดังนั้นจะมีเพียงพวกเขาที่สามารถมองเห็นลำแสงเลเซอร์ได้
หากลำแสงเลเซอร์ถูกสร้างขึ้นบนเซิร์ฟเวอร์ จะมีทุกคนสามารถมองเห็นมันได้ อย่างไรก็ตาม จะมี การหน่วง เล็กน้อยระหว่างที่ลูกค้าส่งเสียงปืนและเซิร์ฟเวอร์ได้รับข้อมูลเกี่ยวกับการยิง สิ่งนี้หมายความว่าลูกค้าที่ยิงอาวุธจะเห็นการหน่วงระหว่างเมื่อพวกเขาทำงานอาวุธและเมื่อพวกเขาเห็นลำแสง; อาวุธจะรู้สึกหน่วงเป็นผล
เพื่อแก้ไขปัญหานี้ ลูกค้าทุกคนจะสร้างลำแสงเลเซอร์ของตัวเอง ซึ่งทำให้ลูกค้าที่ยิงอาวุธจะเห็นลำแสงเลเซอร์ทันที ลูกค้าอื่น ๆ จะมีการหน่วงเล็กน้อยระหว่างเมื่อผู้เล่นคนอื่นยิงและลำแสงปรากฏ นี่คือกรณีที่ดีที่สุด: ไม่มีวิธีการสื่อสารลำแสงเลเซอร์ของลูกค้าหนึ่งไปยังลูกค้าอื่นได้อย่างรวดเร็วกว่า
ลูกค้าที่ยิง
ก่อนอื่น ลูกค้าต้องบอกเซิร์ฟเวอร์ว่าพวกเขาได้ยิงเลเซอร์และให้ตำแหน่งสิ้นสุด
แทรก RemoteEvent ลงในโฟลเดอร์ Events ใน ReplicatedStorage และตั้งชื่อว่า LaserFired.

หาฟังก์ชัน fireWeapon ในสคริปต์ ToolController ที่ด้านล่างของฟังก์ชัน ให้เรียกใช้เหตุการณ์ LaserFired โดยใช้งาน hitPosition เป็นอาร์กิวเมนต์
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
เซิร์ฟเวอร์
เซิร์ฟเวอร์ตอนนี้ต้องรับเหตุการณ์ว่าลูกค้าได้ยิงและบอกให้ลูกค้าทุกคนเกี่ยวกับการยิงเพื่อให้พวกเขาสามารถเรนเดอร์ลำแสงเลเซอร์
ในสคริปต์ ServerLaserManager สร้างฟังก์ชันชื่อ playerFiredLaser ด้านบนของ damageCharacter ที่มีพารามิเตอร์ชื่อ playerFired และ endPosition.
เชื่อมต่อฟังก์ชันกับเหตุการณ์ LaserFired.
-- แจ้งให้ลูกค้าทุกคนทราบว่าลำแสงได้ถูกยิงแล้วเพื่อที่พวกเขาจะสามารถแสดงลำแสงlocal function playerFiredLaser(playerFired, endPosition)end-- เชื่อมต่อเหตุการณ์กับฟังก์ชั่นที่เหมาะสมeventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
เซิร์ฟเวอร์ต้องการจุดเริ่มต้นของเลเซอร์ สิ่งนี้อาจส่งมาจากลูกค้าแต่เป็นที่ดีที่สุดที่จะหลีกเลี่ยงการเชื่อถือจากลูกค้าที่เป็นไปได้ ตำแหน่งจับของอาวุธของตัวละครผู้เล่นจะเป็นตำแหน่งเริ่มต้น ดังนั้นเซิร์ฟเวอร์สามารถหามันจากที่นั่น
สร้างฟังก์ชัน getPlayerToolHandle ด้านบนของฟังก์ชัน playerFiredLaser ด้วยพารามิเตอร์ชื่อ player.
ใช้โค้ดด้านล่างเพื่อตรวจหาจับอาวุธจากตัวละครผู้เล่นและส่งคืนวัตถุจับ
local LASER_DAMAGE = 10-- หา Handle ของอาวุธที่ผู้เล่นถืออยู่local function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- แจ้งให้ลูกค้าทุกคนทราบว่าลำแสงได้ถูกยิงแล้วเพื่อที่พวกเขาจะสามารถแสดงลำแสงlocal function playerFiredLaser(playerFired, endPosition)
เซิร์ฟเวอร์ตอนนี้สามารถเรียก FireAllClients บนเหตุการณ์ LaserFired เพื่อส่งข้อมูลที่จำเป็นสำหรับการเรนเดอร์ลำแสงไปยังลูกค้า สิ่งนี้รวมถึง ผู้เล่น ที่ยิงลำแสง (ดังนั้นลูกค้าสำหรับผู้เล่นนั้นไม่ต้องเรนเดอร์ลำแสงซ้ำสองครั้ง) handle ของบลาสเตอร์ (ทำหน้าที่เป็นตำแหน่งเริ่มต้นสำหรับลำแสง) และตำแหน่งสิ้นสุดของลำแสง.
ในฟังก์ชัน playerFiredLaser, เรียกใช้ฟังก์ชัน getPlayerToolHandle โดยส่ง playerFired เป็นอาร์กิวเมนต์และมอบหมายให้กับตัวแปรชื่อ toolHandle.
หาก toolHandle มีอยู่ ส่งเหตุการณ์ LaserFired ไปยังลูกค้าทุกคนโดยใช้งาน playerFired, toolHandle และ endPosition เป็นอาร์กิวเมนต์.
-- แจ้งให้ลูกค้าทุกคนทราบว่าลำแสงได้ถูกยิงแล้วเพื่อที่พวกเขาจะสามารถแสดงลำแสงlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
เรนเดอร์บนลูกค้า
ตอนนี้ FireAllClients ได้ถูกเรียกใช้งาน ลูกค้าทุกคนจะได้รับเหตุการณ์จากเซิร์ฟเวอร์เพื่อตรวจสอบลำแสงเลเซอร์ ทุกลูกค้าสามารถใช้โมดูล LaserRenderer จากก่อนหน้านี้เพื่อเรนเดอร์ลำแสงเลเซอร์โดยใช้ตำแหน่งของจับและตำแหน่งสิ้นสุดที่ส่งมาจากเซิร์ฟเวอร์ ผู้เล่นที่ยิงลำแสงเลเซอร์ในเบื้องต้นควรละเว้นเหตุการณ์นี้ ไม่เช่นนั้นพวกเขาจะเห็นลำแสงสองชุด
สร้าง LocalScript ลงใน StarterPlayerScripts ตั้งชื่อว่า ClientLaserManager.

ภายในสคริปต์ ให้นำเข้าโมดูล LaserRenderer.
สร้างฟังก์ชันชื่อ createPlayerLaser ที่มีพารามิเตอร์ playerWhoShot, toolHandle และ endPosition.
เชื่อมต่อฟังก์ชันกับเหตุการณ์ LaserFired ในโฟลเดอร์ Events.
ในฟังก์ชัน ใช้ if statement เพื่อตรวจสอบว่า playerWhoShot ไม่เท่ากับ LocalPlayer.
ภายใน if statement, เรียกใช้ฟังก์ชัน 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 thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)ทดสอบบลาสเตอร์ด้วยผู้เล่นสองคนโดยเริ่มเซิร์ฟเวอร์ท้องถิ่น วางลูกค้าแต่ละอันที่ด้านข้างต่างกันของมอนิเตอร์เพื่อให้คุณเห็นทั้งสองหน้าต่างพร้อมกัน เมื่อคุณยิงในลูกค้าหนึ่ง คุณควรเห็นลำแสงในผู้เล่นอีกคนหนึ่ง.
เสียงเอฟเฟกต์
เอฟเฟกต์เสียงการยิงในปัจจุบันเล่นอยู่บนลูกค้าที่ยิงกระสุนเพียงคนเดียว คุณจะต้องย้ายโค้ดเพื่อเล่นเสียงให้กับผู้เล่นอื่นด้วย.
ในสคริปต์ ToolController ให้ไปที่ฟังก์ชัน toolActivated และลบบรรทัดที่เล่นเสียงที่เริ่มใช้งาน.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendที่ด้านล่างของฟังก์ชัน createLaser ใน LaserRenderer, ประกาศตัวแปรชื่อ shootingSound และใช้วิธี FindFirstChild() ของ toolHandle เพื่อตรวจสอบหาสิ่งที่เรียกว่า Activate.
ใช้ if statement เพื่อตรวจสอบว่ามี shootingSound หรือไม่; หากมี, เรียกใช้งานฟังก์ชัน Play() ของมัน.
laserPart.Parent = workspace-- เพิ่มลำแสงเลเซอร์ไปยังบริการ Debris เพื่อลบ & ทำความสะอาดDebris:AddItem(laserPart, SHOT_DURATION)-- เล่นเสียงยิงอาวุธlocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
ป้องกันรีโมตด้วยการตรวจสอบ
หากเซิร์ฟเวอร์ไม่ตรวจสอบข้อมูลจากคำร้องที่เข้ามา แฮ็กเกอร์สามารถใช้ฟังก์ชันรีโมตและอีเวนต์เพื่อส่งค่าที่ปลอมให้กับเซิร์ฟเวอร์ได้ สิ่งสำคัญคือต้องใช้ การตรวจสอบด้านเซิร์ฟเวอร์ เพื่อป้องกันสิ่งนี้
ในรูปแบบปัจจุบัน เหตุการณ์ DamageCharacter ย่อมมีช่องโหว่มาก การโจมตีนี้จะทำให้แฮ็กเกอร์ทำให้เกิดความเสียหายกับผู้เล่นใด ๆ ที่พวกเขาต้องการโดยไม่ยิงใส่พวกเขา
การตรวจสอบคือกระบวนการการตรวจสอบว่าค่าที่ส่งไปยังเซิร์ฟเวอร์นั้นเป็นไปตามจริง ในกรณีนี้ เซิร์ฟเวอร์ต้องการที่จะ:
- ตรวจสอบว่าระยะทางระหว่างผู้เล่นและตำแหน่งที่ถูกโจมตีโดยเลเซอร์นั้นอยู่ภายในขอบเขตที่กำหนด
- เรย์แคสต์ระหว่างอาวุธที่ยิงเลเซอร์และตำแหน่งที่ถูกตีเพื่อให้แน่ใจว่าการยิงนั้นเป็นไปได้และไม่ผ่านกำแพง
ลูกค้า
ลูกค้าต้องส่งตำแหน่งที่โดนเลเซอร์ไปยังเซิร์ฟเวอร์เพื่อให้เช็คระยะได้ที่สมจริง
ใน ToolController, ไปที่บรรทัดที่เหตุการณ์ DamageCharacter ถูกยิงในฟังก์ชัน fireWeapon.
เพิ่ม hitPosition เป็นอาร์กิวเมนต์.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
เซิร์ฟเวอร์
ตอนนี้ลูกค้าส่งพารามิเตอร์เพิ่มเติมผ่านเหตุการณ์ DamageCharacter ดังนั้น ServerLaserManager ต้องมีการปรับปรุงเพื่อรองรับมัน
ในสคริปต์ ServerLaserManager, เพิ่มพารามิเตอร์ hitPosition ในฟังก์ชัน damageCharacter.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- ลบพลังงานจากตัวละครhumanoid.Health -= LASER_DAMAGEendendด้านล่างฟังก์ชัน getPlayerToolHandle, สร้างฟังก์ชันชื่อ isHitValid โดยมีพารามิเตอร์สามพารามิเตอร์: playerFired, characterToDamage และ hitPosition.
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
การตรวจสอบครั้งแรกจะอยู่ที่ระยะห่างระหว่างตำแหน่งที่โดนและตัวละคร
ประกาศตัวแปรชื่อ MAX_HIT_PROXIMITY ที่ด้านบนของโค้ดและกำหนดให้มีค่าเป็น 10 นี่จะเป็นระยะขั้นต่ำที่ได้รับอนุญาตระหว่างการโจมตีและตัวละคร ต้องการความคืบหน้าเนื่องจากตัวละครอาจเลื่อนไปเล็กน้อยตั้งแต่ลูกชายยิงเหตุการณ์.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10ในฟังก์ชัน isHitValid, คำนวณระยะทางระหว่างตัวละครและตำแหน่งที่โดน หากระยะทางมากกว่า MAX_HIT_PROXIMITY ให้ส่งคืน false.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- ตรวจสอบระยะทางระหว่างตัวละครที่ถูกตีและตำแหน่งที่โดนlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
การตรวจสอบครั้งที่สองจะเกี่ยวข้องกับการเรย์แคสต์ระหว่างการยิงและตำแหน่งที่โดน หากการเรย์แคสต์ส่งคืนวัตถุที่ไม่ใช่ตัวละคร คุณสามารถสมมติได้ว่าการยิงไม่น่าจะเกิดขึ้น เนื่องจากอะไรบางอย่างได้บล็อกการยิง.
คัดลอกโค้ดด้านล่างเพื่อตรวจการตรวจสอบนี้ ส่งคืน true ที่ท้ายฟังก์ชัน หากมันดำเนินมาจนถึงที่นั่น การตรวจสอบทั้งหมดได้ผ่านพ้นไปแล้ว.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- อนุญาตให้มีระยะห่างระหว่างตัวละครที่ถูกตีและตำแหน่งที่โดนlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- ตรวจสอบว่าถูกยิงผ่านกำแพงlocal toolHandle = getPlayerToolHandle(playerFired)if toolHandle thenlocal rayLength = (hitPosition - toolHandle.Position).Magnitudelocal rayDirection = (hitPosition - toolHandle.Position).Unitlocal raycastParams = RaycastParams.new()raycastParams.FilterDescendantsInstances = {playerFired.Character}local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)-- ถ้าพบอินสแตนซ์ที่ไม่ใช่ตัวละคร ให้มองข้ามการยิงif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendประกาศตัวแปรในฟังก์ชัน damageCharacter ชื่อ validShot. มอบหมายค่าให้กับผลลัพธ์ของการเรียกฟังก์ชัน isHitValid โดยมีพารามิเตอร์สามอัน: playerFired, characterToDamage และ hitPosition.
ใน if statement ด้านล่าง ให้เพิ่มตัวดำเนินการ 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_DAMAGEendend
ตอนนี้เหตุการณ์ 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
-- หา Handle ของอาวุธที่ผู้เล่นถืออยู่
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)