ตรวจจับการโจมตี

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

การตรวจจับการโจมตี เป็นกระบวนการในการระบุเมื่อกระสุนชนกับผู้เล่น จากนั้นลดพลังชีวิตของพวกเขาตามนั้น โดยรวมแล้ว คุณสามารถนึกถึงงานนี้ว่าเป็น:

  1. การตรวจสอบที่จำลองทางกายภาพว่าโปรเจกไทล์ได้ชนกับเป้าหมายหรือไม่
  2. การตรวจสอบอย่างทันทีว่าอาวุธยิงถูกเล็งไปที่เป้าหมายหรือไม่

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

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

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

หลังจากที่คุณทำส่วนนี้เสร็จแล้ว คุณสามารถสำรวจหัวข้อการพัฒนาที่เพิ่มเติมเพื่อปรับปรุงประสบการณ์เล่นของคุณ เช่น เสียง แสง และเอฟเฟกต์พิเศษ

รับทิศทางการยิง

หลังจากที่ผู้เล่นยิงอาวุธไปแล้ว ReplicatedStorageBlasterattemptBlastClientblastClientgenerateBlastData จะเรียกใช้งานฟังก์ชันสองตัวเพื่อเริ่มกระบวนการตรวจจับการโจมตี: rayDirections() และ rayResults().

generateBlastData

local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)
local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)

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

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

getDirectionsForBlast

if numLasers == 1 then
-- สำหรับเลเซอร์เดี่ยว การเล็งตรงไปข้างหน้า
table.insert(directions, originCFrame.LookVector)
elseif numLasers > 1 then
-- สำหรับเลเซอร์หลายตัว ให้กระจายออกไปอย่างสม่ำเสมอในแนวนอน
-- ในช่วงเลเซอร์SpreadDegrees รอบๆ ศูนย์กลาง
local leftAngleBound = laserSpreadDegrees / 2
local rightAngleBound = -leftAngleBound
local degreeInterval = laserSpreadDegrees / (numLasers - 1)
for angle = rightAngleBound, leftAngleBound, degreeInterval do
local direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVector
table.insert(directions, direction)
end
end

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


if numLasers == 1 then
table.insert(directions, originCFrame.LookVector)
elseif numLasers > 1 then
local leftAngleBound = laserSpreadDegrees / 2
local rightAngleBound = -leftAngleBound
local degreeInterval = laserSpreadDegrees / (numLasers - 1)
for angle = rightAngleBound, leftAngleBound, degreeInterval do
local direction
if spreadDirection == "vertical" then
direction = (originCFrame * CFrame.Angles(math.rad(angle), 0, 0)).LookVector
else
direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVector
end
table.insert(directions, direction)
end
end
return directions

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

generateBlastData

local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)
for _, direction in rayDirections do -- new line
print(direction) -- new line
end -- new line
local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)

ยิงลำแสง

castLaserRay(), ฟังก์ชันที่สองใน ReplicatedStorageBlasterattemptBlastClientblastClientgenerateBlastData, ทำการดำเนินการที่ซับซ้อนมากขึ้นภายในสคริปต์ มันเริ่มต้นโดยการระบุพารามิเตอร์เพื่อให้สามารถทำ Workspace:Raycast() เรียกใช้งานสำหรับวัตถุประสงค์ในการยิงลำแสง การยิงลำแสงคือกระบวนการในการส่งลำแสงที่มองไม่เห็นจากจุด Vector3 ในทิศทางเฉพาะพร้อมกับความยาวที่กำหนด จากนั้นตรวจสอบเส้นทางของมันเพื่อดูว่าชนกับวัตถุอื่นที่ไหน

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

รูปภาพแสดงภาพที่ Ray A ความต่อเนื่องผ่านกำแพง ในขณะที่ Ray B ชนกับกำแพง.

พารามิเตอร์ของ castLaserRay() กำหนดว่าสิ่งที่เรียกว่า Raycast() ต้องพิจารณาทุกส่วนในพื้นที่ทำงาน ยกเว้น ตัวละครที่ทำการยิง สคริปต์จะยิงลำแสงตามทิศทางในตาราง directions หากลำแสงชนกับสิ่งใดสิ่งหนึ่ง มันจะสร้าง RaycastResult ซึ่งมีห้าแอตทริบิวต์:

  • Distance – ระยะห่างระหว่างจุดเริ่มต้นของลำแสงและจุดตัด
  • InstanceBasePart หรือเซลล์ Terrain ที่ลำแสงตัดกัน
  • MaterialEnum.Material ที่จุดตัด
  • Position – ตำแหน่ง Vector3 ของจุดตัดระหว่างลำแสงและ Instance
  • NormalVector3 ของเวกเตอร์ปกติของด้านที่ลำแสงตัดกัน

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

castLaserRay() จะใช้ Position และ Normal เพื่อสร้าง CFrame ใหม่ที่เรียกว่าจุดหมายของลำแสง ทุกลำแสงมีจุดหมาย และมันจะเป็นที่ที่ลำแสงชนในพื้นที่ 3D หรือจุดที่อยู่ที่ปลายระยะสูงสุดของมัน ขึ้นอยู่กับว่า ผู้เล่นของคุณเล็งได้ดีแค่ไหน ท่ามกลางค่าของ taggedPlayer จะมีค่าหลายค่าหรือส่วนใหญ่เป็น nil.

castLaserRay

if result then
-- การยิงชนกับสิ่งใดสิ่งหนึ่ง ให้ตรวจสอบว่ามันเป็นผู้เล่นหรือไม่
destination = CFrame.lookAt(result.Position, result.Position + result.Normal)
taggedPlayer = getPlayerFromDescendant(result.Instance)
else
-- การยิงไม่ชนกับอะไรเลย จุดหมายของมันคือ
-- จุดที่ระยะทางสูงสุด
local distantPosition = origin + rayDirection * MAX_DISTANCE
destination = CFrame.lookAt(distantPosition, distantPosition - rayDirection)
taggedPlayer = nil
end

ตรวจสอบการยิง

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

  1. ก่อนอื่น getValidatedRayResults จะเรียกใช้งาน validateRayResult เพื่อตรวจสอบว่าทุกรายการในตาราง rayResults จากไคลเอนต์เป็น CFrame และผู้เล่น (หรือ nil).

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

    เช่นเดียวกับการตรวจสอบอาวุธจากบทก่อนหน้า isRayAngleFromOriginValid จะพึ่งพาค่าความอดทนเพื่อกำหนดว่าสิ่งที่เป็น "ความแตกต่างมากเกินไป" ในมุม:

    isRayAngleFromOriginValid

    local claimedDirection = (rayResult.destination.Position - originCFrame.Position).Unit
    local directionErrorDegrees = getAngleBetweenDirections(claimedDirection, expectedDirection)
    return directionErrorDegrees <= ToleranceValues.BLAST_ANGLE_SANITY_CHECK_TOLERANCE_DEGREES

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

    getAngleBetweenDirections

    local function getAngleBetweenDirections(directionA: Vector3, directionB: Vector3)
    local dotProduct = directionA:Dot(directionB)
    local cosAngle = math.clamp(dotProduct, -1, 1)
    local angle = math.acos(cosAngle)
    return math.deg(angle)
    end
  3. การตรวจสอบถัดไปเป็นเรื่องที่เข้าใจได้ง่ายที่สุด ขณะที่ getValidatedBlastData ใช้ DISTANCE_SANITY_CHECK_TOLERANCE_STUDS เพื่อตรวจสอบว่าผู้เล่นที่ยิงอยู่ใกล้กับจุดกำเนิดของลำแสงหรือไม่ isPlayerNearPosition ใช้ตรรกะเดียวกันเพื่อตรวจสอบว่าผู้เล่นที่ถูกแท็กอยู่ใกล้กับจุดหมายของลำแสงหรือไม่:

    isPlayerNearPosition

    local distanceFromCharacterToPosition = position - character:GetPivot().Position
    if distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS then
    return false
    end
  4. การตรวจสอบสุดท้าย isRayPathObstructed ใช้การสร้างแบบใหม่จากการยิงลำแสงเพื่อตรวจสอบว่าจุดหมายของลำแสงนั้นอยู่หลังจากกำแพงหรือสิ่งกีดขวางอื่นจากตำแหน่งของไคลเอนต์หรือไม่ ตัวอย่างเช่น หากผู้เล่นที่โกงพยายามลบกำแพงทั้งหมดจากประสบการณ์เพื่อแท็กผู้เล่นอื่น เซิร์ฟเวอร์จะตรวจสอบและยืนยันว่าลำแสงนั้นไม่ถูกต้องเพราะมันรู้ตำแหน่งของทุกวัตถุภายในสภาพแวดล้อม

    isRayPathObstructed

    local scaledDirection = (rayResult.destination.Position - blastData.originCFrame.Position)
    scaledDirection *= (scaledDirection.Magnitude - 1) / scaledDirection.Magnitude

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

ลดพลังชีวิตของผู้เล่น

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

เริ่มต้นด้วยการลดพลังชีวิตของผู้เล่นที่ถูกแท็ก คู่มือ Spawning and respawning แสดงความแตกต่างระหว่าง Player และ Player.Character โดยเฉพาะว่า ตัวละครเป็นโมเดล Humanoid โมเดล Humanoid มีคุณสมบัติ Health โดยมีค่าดีฟอลต์เป็น 100 แทนที่จะสร้างระบบของตัวเอง ประสบการณ์เลเซอร์แท็กตัวอย่างใช้คุณสมบัติในตัวนี้เพื่อติดตามว่าผู้เล่นต้องการความเสียหายเท่าไรเพื่อให้ถูกแท็กออกจากรอบ

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

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

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

onPlayerTagged

local function onPlayerTagged(playerBlasted: Player, playerTagged: Player, damageAmount: number)
local character = playerTagged.Character
local isFriendly = playerBlasted.Team == playerTagged.Team
-- ห้ามยิงเพื่อน
if isFriendly then
return
end
local humanoid = character and character:FindFirstChild("Humanoid")
if humanoid and humanoid.Health > 0 then
-- ป้องกันไม่ให้สุขภาพเป็นค่าติดลบ
local damage = math.min(damageAmount, humanoid.Health)
-- TakeDamage รับรองว่าไม่ให้สุขภาพลดลงหากฟอร์ซฟิลด์ใช้งานอยู่
humanoid:TakeDamage(damage)
if humanoid.Health <= 0 then
-- มอบคะแนนให้กับ playerBlasted สำหรับการแท็ก playerTagged
Scoring.incrementScore(playerBlasted, 1)
end
end
end

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

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

  • ภาพลักษณ์ของอาวุธ: ดูที่ ReplicatedStorageFirstPersonBlasterVisuals และ ServerScriptServiceThirdPersonBlasterVisuals.
  • เสียง: ดูที่ ReplicatedStorageSoundHandler.
  • โหมดที่ปรับแต่งได้: คุณสามารถปรับปรุงประสบการณ์นี้ให้เปิดเผยประเภทของวัตถุประสงค์ใหม่ๆ เช่น การทำคะแนนให้ได้มากที่สุดก่อนเวลาจะหมดได้อย่างไร?

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

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