การชนเกิดขึ้นเมื่อวัตถุ 3 มิติสองชิ้นสัมผัสกันในโลก 3 มิติ สำหรับการจัดการการชนที่กำหนดเอง BasePart มีชุดของ เหตุการณ์การชน และเทคนิค การกรองการชน เพื่อให้คุณสามารถควบคุมว่าวัตถุทางกายภาพใดชนกับวัตถุอื่น
เหตุการณ์การชน
เหตุการณ์การชน เกิดขึ้น เมื่อ BaseParts สองชิ้นสัมผัสกันหรือหยุดสัมผัสกันในโลก 3 มิติ คุณสามารถตรวจจับการชนเหล่านี้ได้ผ่าน Touched และ TouchEnded ซึ่งเกิดขึ้นไม่ว่าจะมีค่า CanCollide ของแต่ละชิ้นส่วนเป็นอย่างไร เมื่อพิจารณาการจัดการการชนบนชิ้นส่วน ให้สังเกตสิ่งต่อไปนี้:
- ค่า CanTouch ของชิ้นส่วนจะกำหนดว่ามันจะกระตุ้นเหตุการณ์การชนหรือไม่ หากตั้งค่าเป็น false จะไม่มีการเรียกใช้งาน Touched หรือ TouchEnded
- ค่า CanCollide ของชิ้นส่วนจะมีผลต่อว่ามันจะ ชนทางกายภาพ กับชิ้นส่วนอื่นหรือไม่และทำให้เกิดแรงกระทำต่อพวกมัน แม้ว่าจะปิดการใช้งาน CanCollide สำหรับชิ้นส่วน คุณยังสามารถตรวจจับการสัมผัสและการไม่สัมผัสได้ผ่านเหตุการณ์ Touched และ TouchEnded
- เหตุการณ์ Touched และ TouchEnded จะเกิดขึ้นเฉพาะจากการเคลื่อนไหว ทางกายภาพ ไม่ใช่จากการเปลี่ยนแปลง Position หรือ CFrame ที่ทำให้ชิ้นส่วนตัดกันหรือหยุดตัดกันกับชิ้นส่วนอื่น
Touched
เหตุการณ์ Touched จะเกิดขึ้นเมื่อ BasePart สัมผัสกับอีกชิ้นหนึ่ง หรือกับว็อกเซล Terrain มันจะเกิดขึ้นเฉพาะจาก การจำลองทางกายภาพ และจะไม่เกิดขึ้นเมื่อค่า Position หรือ CFrame ของชิ้นส่วนถูกตั้งค่าอย่างชัดเจนให้ตัดกันกับชิ้นส่วนอื่นหรือว็อกเซล
รูปแบบโค้ดต่อไปนี้แสดงให้เห็นว่าเหตุการณ์ Touched สามารถเชื่อมต่อกับฟังก์ชัน onTouched() ที่กำหนดเองได้อย่างไร โปรดทราบว่าเหตุการณ์จะส่งอาร์กิวเมนต์ otherPart ไปยังฟังก์ชัน ซึ่งบ่งบอกถึงชิ้นส่วนอื่นที่เกี่ยวข้องในการชน
local part = workspace.Part
local function onTouched(otherPart)
print(part.Name .. " ชนกับ " .. otherPart.Name)
end
part.Touched:Connect(onTouched)โปรดทราบว่าเหตุการณ์ Touched อาจเกิดขึ้นหลายครั้งในระยะเวลาสั้น ๆ ขึ้นอยู่กับการชนทางกายภาพที่ละเอียดอ่อน เช่น เมื่อวัตถุที่เคลื่อนที่ "ตั้งอยู่" ในตำแหน่งที่พักหรือเมื่อการชนเกี่ยวข้องกับ โมเดลหลายชิ้นส่วน เพื่อหลีกเลี่ยงการกระตุ้นเหตุการณ์ Touched มากเกินไป คุณสามารถใช้ระบบดีบาวน์ซ์ง่าย ๆ ที่บังคับใช้ "ระยะเวลาพัก" ผ่าน แอตทริบิวต์ ของอินสแตนซ์
local part = workspace.Part
local COOLDOWN_TIME = 1
local function onTouched(otherPart)
if not part:GetAttribute("Touched") then
print(part.Name .. " ชนกับ " .. otherPart.Name)
part:SetAttribute("Touched", true) -- ตั้งค่าแอตทริบิวต์เป็น true
task.wait(COOLDOWN_TIME) -- รอระยะเวลาพัก
part:SetAttribute("Touched", false) -- รีเซ็ตแอตทริบิวต์
end
end
part.Touched:Connect(onTouched)TouchEnded
เหตุการณ์ TouchEnded จะเกิดขึ้นเมื่อขอบเขตการชนทั้งหมดของ BasePart ออกจากขอบเขตของ BasePart อื่นหรือว็อกเซล Terrain ที่เต็ม มันจะเกิดขึ้นเฉพาะจาก การจำลองทางกายภาพ และจะไม่เกิดขึ้นเมื่อค่า Position หรือ CFrame ของชิ้นส่วนถูกตั้งค่าอย่างชัดเจนให้หยุดตัดกันกับชิ้นส่วนอื่นหรือว็อกเซล
รูปแบบโค้ดต่อไปนี้แสดงให้เห็นว่าเหตุการณ์ TouchEnded สามารถเชื่อมต่อกับฟังก์ชัน onTouchEnded() ที่กำหนดเองได้อย่างไร เช่นเดียวกับ Touched เหตุการณ์จะส่งอาร์กิวเมนต์ otherPart ไปยังฟังก์ชัน ซึ่งบ่งบอกถึงชิ้นส่วนอื่นที่เกี่ยวข้อง
local part = workspace.Part
local function onTouchEnded(otherPart)
print(part.Name .. " ไม่ได้สัมผัสกับ " .. otherPart.Name อีกต่อไป)
end
part.TouchEnded:Connect(onTouchEnded)การกรองการชน
การกรองการชน กำหนด ว่าวัตถุทางกายภาพใดชนกับวัตถุอื่น คุณสามารถกำหนดการกรองสำหรับวัตถุจำนวนมากผ่าน กลุ่มการชน หรือคุณสามารถควบคุมการชนในระดับ ชิ้นส่วนต่อชิ้นส่วน ด้วยอินสแตนซ์ NoCollisionConstraint
กลุ่มการชน
กลุ่มการชน อนุญาต ให้คุณกำหนด BaseParts ให้กับกลุ่มเฉพาะและระบุว่าพวกมันจะชนกับกลุ่มอื่นหรือไม่ ชิ้นส่วนภายในกลุ่มที่ไม่ชนจะผ่านกันไปโดยสมบูรณ์ แม้ว่าชิ้นส่วนทั้งสองจะมีค่า CanCollide ตั้งเป็น true กลุ่มการชนจะถูกสร้างและกำหนดค่าบนพื้นฐานของ Workspace หรือ WorldModel
คุณสามารถตั้งค่ากลุ่มการชนได้อย่างง่ายดายผ่านตัวแก้ไข กลุ่มการชน ของ Studio ซึ่งสามารถเข้าถึงได้ผ่านเมนู หน้าต่าง ⟩ 3D
ลงทะเบียนกลุ่ม
Workspace เก็บสถานะการกำหนดค่าของกลุ่มการชนของตนเอง นอกจากนี้ WorldModel ใด ๆ สามารถเลือกใช้กลุ่มการชนของตนเองสำหรับการค้นหา
ตัวแก้ไขรวมถึงกลุ่มการชน เริ่มต้น หนึ่งกลุ่มซึ่งไม่สามารถเปลี่ยนชื่อหรือถูกลบได้ BaseParts ทั้งหมดจะอยู่ในกลุ่มเริ่มต้นนี้โดยอัตโนมัติ เว้นแต่จะถูกกำหนดให้กับกลุ่มอื่น ซึ่งหมายความว่าพวกมันจะชนกับวัตถุอื่นทั้งหมดในกลุ่ม เริ่มต้น
ในการสร้างกลุ่มการชนใหม่:
โดยค่าเริ่มต้น กลุ่มจะถูกลงทะเบียนใน Workspace แต่ถ้าคุณมี WorldModel อย่างน้อยหนึ่งตัวในสถานที่ของคุณ เมนูแบบเลื่อนจะปรากฏที่ด้านบนของตัวแก้ไขกลุ่มการชน ใช้เมนูแบบเลื่อนเพื่อกำหนดกลุ่มการชนใน WorldModel ที่แตกต่างกัน

สำหรับ WorldModel ใด ๆ คุณสามารถเปิดใช้งาน ใช้กลุ่มการชนของ Workspace เพื่อให้มันใช้กลุ่มการชนของ Workspace สำหรับการค้นหาของมัน

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

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

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

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

เมื่อกำหนดแล้ว กลุ่มใหม่จะสะท้อนอยู่ภายใต้คุณสมบัติ CollisionGroup ของวัตถุ

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

สำหรับโค้ดปลั๊กอิน แนะนำให้คุณกำหนด "StudioSelectable" เป็นตัวกรองกลุ่มการชนของ RaycastParams ของคุณเมื่อค้นหาชิ้นส่วนใต้เคอร์เซอร์ สิ่งนี้ช่วยให้ปลั๊กอินของคุณตรงตามกลไกการเลือกที่ผู้สร้างได้เรียนรู้ที่จะคาดหวังจากเครื่องมือใน Studio ที่สร้างขึ้น
local UserInputService = game:GetService("UserInputService")
local raycastParams = RaycastParams.new()
raycastParams.CollisionGroup = "StudioSelectable" -- เพื่อปฏิบัติตามแนวทาง
raycastParams.BruteForceAllSlow = true -- เพื่อให้ชิ้นส่วนที่มี CanQuery เป็น "false" สามารถถูกเลือกได้
local mouseLocation = UserInputService:GetMouseLocation()
local mouseRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
local filteredSelectionHit = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 10000, raycastParams)การกรองชิ้นส่วนต่อชิ้นส่วน
เพื่อป้องกันไม่ให้เกิดการชนระหว่างชิ้นส่วนสองชิ้นโดยเฉพาะโดยไม่ต้องตั้งค่ากลุ่มการชน กลุ่มการชน เช่น ระหว่างล้อของรถยนต์และตัวถังของมัน ให้พิจารณาใช้ข้อจำกัด No Collision ข้อดีรวมถึง:
- ไม่จำเป็นต้องมีกลุ่มการชนและ/หรือสคริปต์การกำหนดค่า ดังนั้นคุณจึงสามารถสร้างและแชร์โมเดลที่มีการกรองการชนที่กำหนดเองได้อย่างง่ายดาย
- ชิ้นส่วนที่เชื่อมต่อกันจะไม่ชนกัน แต่ยังสามารถชนกับวัตถุอื่นได้
ปิดการชนของตัวละคร
ตัวละครของผู้เล่น Roblox จะชนกันโดยค่าเริ่มต้น สิ่งนี้อาจนำไปสู่การเล่นเกมที่น่าสนใจแต่ไม่ตั้งใจ เช่น ตัวละครกระโดดขึ้นไปบนกันเพื่อไปยังพื้นที่เฉพาะ หากพฤติกรรมนี้ไม่ต้องการ คุณสามารถป้องกันได้ผ่าน Script ต่อไปนี้ใน ServerScriptService
local Players = game:GetService("Players")
workspace:RegisterCollisionGroup("Characters")
workspace:CollisionGroupSetCollidable("Characters", "Characters", false)
local function onDescendantAdded(descendant)
-- ตั้งค่ากลุ่มการชนสำหรับชิ้นส่วนใด ๆ ที่เป็นลูกหลาน
if descendant:IsA("BasePart") then
descendant.CollisionGroup = "Characters"
end
end
local function onCharacterAdded(character)
-- ประมวลผลลูกหลานที่มีอยู่และใหม่สำหรับการตั้งค่าฟิสิกส์
for _, descendant in character:GetDescendants() do
onDescendantAdded(descendant)
end
character.DescendantAdded:Connect(onDescendantAdded)
end
Players.PlayerAdded:Connect(function(player)
-- ตรวจจับเมื่อมีการเพิ่มตัวละครของผู้เล่น
player.CharacterAdded:Connect(onCharacterAdded)
end)การชนของโมเดล
วัตถุ Model เป็นที่เก็บสำหรับชิ้นส่วนแทนที่จะสืบทอดจาก BasePart ดังนั้นพวกมันจึงไม่สามารถเชื่อมต่อกับเหตุการณ์ BasePart.Touched หรือ BasePart.TouchEnded ได้โดยตรง เพื่อกำหนดว่าว่าโมเดลจะกระตุ้นเหตุการณ์การชนหรือไม่ คุณต้องวนรอบลูกหลานของมันและเชื่อมต่อฟังก์ชัน onTouched() และ onTouchEnded() ที่กำหนดเองกับแต่ละ BasePart
ตัวอย่างโค้ดต่อไปนี้เชื่อมต่อ BaseParts ทั้งหมดของโมเดลหลายชิ้นส่วนกับเหตุการณ์การชนและติดตามจำนวนการชนทั้งหมดกับชิ้นส่วนอื่น
local model = script.Parent
local numTouchingParts = 0
local function onTouched(otherPart)
-- ละเว้นกรณีที่โมเดลตัดกันกับตัวเอง
if otherPart:IsDescendantOf(model) then return end
-- เพิ่มจำนวนชิ้นส่วนของโมเดลที่สัมผัส
numTouchingParts += 1
print(model.Name, "ตัดกันกับ", otherPart.Name, "| ชิ้นส่วนของโมเดลที่สัมผัส:", numTouchingParts)
end
local function onTouchEnded(otherPart)
-- ละเว้นกรณีที่โมเดลไม่ตัดกันกับตัวเอง
if otherPart:IsDescendantOf(model) then return end
-- ลดจำนวนชิ้นส่วนของโมเดลที่สัมผัส
numTouchingParts -= 1
print(model.Name, "ไม่ตัดกันกับ", otherPart.Name, "| ชิ้นส่วนของโมเดลที่สัมผัส:", numTouchingParts)
end
for _, child in model:GetChildren() do
if child:IsA("BasePart") then
child.Touched:Connect(onTouched)
child.TouchEnded:Connect(onTouchEnded)
end
endการชนของเมชและโมเดลแข็ง
MeshPart และ PartOperation (ชิ้นส่วนที่เชื่อมต่อกันโดย การสร้างแบบแข็ง) เป็นซับคลาสของ BasePart ดังนั้นเมชและชิ้นส่วนที่สร้างแบบแข็งจึงสืบทอดเหตุการณ์การชนและตัวเลือกการกรองการชนเดียวกันกับชิ้นส่วนปกติ อย่างไรก็ตาม เนื่องจากเมชและชิ้นส่วนที่สร้างแบบแข็งมักมีรูปทรงเรขาคณิตที่ซับซ้อนมากขึ้น พวกมันจึงมีคุณสมบัติ CollisionFidelity ที่โดดเด่นซึ่งกำหนดว่าขอบเขตทางกายภาพจะตรงกับการแสดงผลทางสายตาอย่างไรสำหรับการจัดการการชน
คุณสมบัติ CollisionFidelity มีตัวเลือกดังต่อไปนี้ โดยเรียงตามความแม่นยำและผลกระทบต่อประสิทธิภาพจากต่ำไปสูง:
- กล่อง — สร้างกล่องการชนที่มีขนาดพอเหมาะ เหมาะสำหรับวัตถุขนาดเล็กหรือไม่โต้ตอบ
- ฮัลล์ — สร้างฮัลล์นูน เหมาะสำหรับวัตถุที่มีรอยบุ๋มหรือโพรงน้อย
- เริ่มต้น — สร้างรูปร่างการชนที่ประมาณการซึ่งรองรับความโค้ง เหมาะสำหรับวัตถุที่ซับซ้อนที่มีความต้องการการโต้ตอบที่มีรายละเอียดปานกลาง
- การแยกที่แม่นยำ — เสนอความแม่นยำสูงสุดแต่ยังไม่ใช่การแสดงผล 1:1 ของการแสดงผล ตัวเลือกนี้มีค่าใช้จ่ายด้านประสิทธิภาพที่แพงที่สุดและใช้เวลานานกว่าในการคำนวณของเอนจิน

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