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

ค้นหาตำแหน่งเมาส์
ก่อนที่เลเซอร์จะถูกยิง คุณต้องรู้ก่อนว่าผู้เล่นกำลังเล็งไปที่ไหน สิ่งนี้สามารถค้นหาได้โดยการเรย์แคสจากตำแหน่งเมาส์ 2D ของผู้เล่นบนหน้าจอไปข้างหน้าโดยตรงจากกล้องเข้าสู่โลก 3D รังสีจะชนกับสิ่งที่ผู้เล่นเล็งด้วยเมาส์
เปิดสคริปต์ ToolController ภายในเครื่องมือบลาสเตอร์จาก สร้างเครื่องมือสำหรับผู้เล่น หากคุณยังไม่ได้ทำบทเรียนนี้ คุณสามารถดาวน์โหลดโมเดล 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 เพื่อตรวจสอบว่า 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
ทำให้เวกเตอร์มีขนาด 1 โดยใช้คุณสมบัติ Unit ของมัน ซึ่งทำให้มันง่ายต่อการคูณด้วยความยาวในภายหลัง
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 เพื่อตรวจสอบว่า 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 ของ 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 ทุกครั้งที่การดำเนินการเรย์แคสชนกับผู้เล่นคนอื่น
ทดสอบกับผู้เล่นหลายคน
ต้องการผู้เล่นสองคนเพื่อตรวจสอบว่าเรย์แคสอาวุธพบผู้เล่นคนอื่นหรือไม่ ดังนั้นคุณต้องเริ่มเซิร์ฟเวอร์ท้องถิ่น ดูที่ การจำลองหลายคลายเอนต์ สำหรับคำแนะนำ
ในหนึ่งไคลเอนต์ ทดสอบการยิงผู้เล่นอีกคนด้วยอาวุธโดยการคลิกที่พวกเขา "ผู้เล่นถูกชน" ควรแสดงในผลลัพธ์ทุกครั้งที่ผู้เล่นถูกยิง
ค้นหาตำแหน่งเลเซอร์
บลาสเตอร์ควรยิงลำแสงสีแดงไปยังเป้าหมาย ฟังก์ชันสำหรับสิ่งนี้จะอยู่ภายใน 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 เรียกใช้ฟังก์ชัน LaserRenderer createLaser โดยใช้มือเครื่องมือและ 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 และเรียกใช้ canShootWeapon เพื่อตรวจสอบว่าอาวุธสามารถยิงได้หรือไม่
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()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 ด้วยบรรทัดของ Luau เพื่อยิงเหตุการณ์ 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)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
ใช้โค้ดต่อไปนี้เพื่อค้นหาตัวละครของผู้เล่นสำหรับอาวุธและส่งคืนวัตถุ handle
local LASER_DAMAGE = 10-- ค้นหามือของเครื่องมือที่ผู้เล่นถืออยู่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 เพื่อตรวจสอบว่า playerWhoShot ไม่เท่ากับ LocalPlayer
ภายในคำสั่ง 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 thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)ทดสอบบลาสเตอร์กับผู้เล่น 2 คนโดยเริ่มเซิร์ฟเวอร์ท้องถิ่น วางไคลเอนต์แต่ละคนไว้ที่ด้านต่าง ๆ ของจอภาพของคุณเพื่อให้คุณสามารถเห็นทั้งสองหน้าต่างได้พร้อมกัน เมื่อคุณยิงในไคลเอนต์หนึ่ง คุณควรเห็นลำแสงในไคลเอนต์อื่น
เสียงเอฟเฟกต์
เสียงเอฟเฟกต์การยิงในปัจจุบันจะเล่นเฉพาะในไคลเอนต์ที่ยิงกระสุนเท่านั้น คุณจะต้องย้ายโค้ดเพื่อเล่นเสียงเพื่อให้ผู้เล่นคนอื่นได้ยินด้วย
ในสคริปต์ ToolController ไปที่ฟังก์ชัน toolActivated และลบบรรทัดที่เล่นเสียง Activate
local function toolActivated()if canShootWeapon() thenfireWeapon()endendที่ด้านล่างของฟังก์ชัน createLaser ใน LaserRenderer ประกาศตัวแปรชื่อ shootingSound และใช้วิธีการ FindFirstChild() ของ toolHandle เพื่อตรวจสอบเสียง Activate
ใช้คำสั่ง if เพื่อตรวจสอบว่า shootingSound มีอยู่หรือไม่ หากมี ให้เรียกใช้ฟังก์ชัน Play()
laserPart.Parent = workspace-- เพิ่มลำแสงเลเซอร์ไปยังบริการ Debris เพื่อให้ถูกลบและทำความสะอาดDebris:AddItem(laserPart, SHOT_DURATION)-- เล่นเสียงยิงของอาวุธlocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
ป้องกัน remotes โดยใช้การตรวจสอบ
หากเซิร์ฟเวอร์ไม่ตรวจสอบข้อมูลจากคำขอที่เข้ามา แฮกเกอร์สามารถใช้ฟังก์ชันและเหตุการณ์ระยะไกลเพื่อส่งค่าปลอมไปยังเซิร์ฟเวอร์ได้ เป็นสิ่งสำคัญที่จะต้องใช้ การตรวจสอบด้านเซิร์ฟเวอร์ เพื่อป้องกันสิ่งนี้
ในรูปแบบปัจจุบัน เหตุการณ์ 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 ด้านล่าง เพิ่มตัวดำเนินการ 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 LaserRendererServerLaserManager
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)