การชนกันเกิดขึ้นเมื่อวัตถุ 3D สองชิ้นติดต่อกันในโลก 3D สำหรับการจัดการการชนกันที่ปรับแต่งได้ BasePart มีชุดของ เหตุการณ์การชนกัน และ การกรองการชนกัน เพื่อให้คุณควบคุมว่าการประกอบทางกายภาพใดจะชนกับวัตถุอื่น
เหตุการณ์การชนกัน
เหตุการณ์การชนกัน (events) เกิดขึ้นเมื่อ BaseParts สองชิ้นสัมผัสหรือหยุดสัมผัสในโลก 3D คุณสามารถตรวจจับการชนกันเหล่านี้ได้ผ่านเหตุการณ์ Touched และ TouchEnded ซึ่งเกิดขึ้นโดยไม่คำนึงถึงค่า CanCollide ของแต่ละส่วน เมื่อพิจารณาการจัดการการชนกันบนส่วน ให้สังเกตสิ่งต่อไปนี้:
- ค่า CanTouch ของส่วนจะกำหนดว่ามันจะกระตุ้นเหตุการณ์การชนกันหรือไม่ หากตั้งค่าเป็น false จะไม่เกิดเหตุการณ์ Touched หรือ TouchEnded
- ค่า CanCollide ของส่วนจะมีผลต่อว่ามันจะชนกัน ทางกายภาพ กับส่วนอื่น ๆ และทำให้เกิดแรงที่จะทำงานกับพวกมัน แม้ว่า CanCollide จะถูกปิดใช้งานสำหรับส่วน คุณยังสามารถตรวจจับการสัมผัสและการไม่สัมผัสได้ผ่านเหตุการณ์ Touched และ TouchEnded
- เหตุการณ์ Touched และ TouchEnded จะเกิดขึ้นเฉพาะผลจากการเคลื่อนไหว ทางกายภาพ ไม่ใช่จากการเปลี่ยนแปลงค่า Position หรือ CFrame ที่ทำให้ส่วนตัดกันหรือหยุดตัดกันกับส่วนอื่น
ถูกสัมผัส
เหตุการณ์ Touched จะเกิดขึ้นเมื่อ BasePart ติดต่อกับอีกส่วนหนึ่งหรือกับ voxel Terrain มันจะเกิดขึ้นเฉพาะจากการ จำลองทางกายภาพ และจะไม่เกิดขึ้นเมื่อค่า Position หรือ CFrame ของส่วนถูกตั้งค่าชัดเจนให้สัมผัสกับอีกส่วนหรือ voxel
โค้ดตัวอย่างต่อไปนี้แสดงวิธีที่เหตุการณ์ Touched สามารถเชื่อมโยงกับฟังก์ชัน onTouched() ที่กำหนดเอง หมายเหตุว่าเหตุการณ์ส่งอาร์กิวเมนต์ otherPart ไปยังฟังก์ชัน ซึ่งระบุถึงส่วนอื่นที่เกี่ยวข้องกับการชนกัน
การชนกันของส่วน
local Workspace = game:GetService("Workspace")
local part = Workspace.Part
local function onTouched(otherPart)
print(part.Name .. " ชนกับ " .. otherPart.Name)
end
part.Touched:Connect(onTouched)
โปรดทราบว่าเหตุการณ์ Touched อาจเกิดขึ้นหลายครั้งติดต่อกันอย่างรวดเร็วตามการชนกันทางกายภาพที่ละเอียด เช่น เมื่อวัตถุที่เคลื่อนที่ "ตั้งตัว" ในตำแหน่งที่พัก หรือเมื่อการชนกันเกี่ยวข้องกับ โมเดลหลายส่วน เพื่อหลีกเลี่ยงการกระตุ้นเหตุการณ์ Touched มากเกินไปไม่จำเป็น คุณสามารถใช้ระบบการหน่วงที่ง่ายดายซึ่งบังคับใช้ "ระยะผ่อนคลาย" ผ่านอาร์ตริบิวต์ instance
การชนกันของส่วนพร้อมระยะผ่อนคลาย
local Workspace = game:GetService("Workspace")
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 จะเกิดขึ้นเมื่อขอบเขตการชนทั้งหมดของ BasePart ออกจากขอบเขตของ BasePart อื่นหรือ voxel Terrain ที่ถูกเติมเต็ม มันจะเกิดขึ้นเฉพาะจากการ จำลองทางกายภาพ และจะไม่เกิดขึ้นเมื่อค่า Position หรือ CFrame ของส่วนถูกตั้งค่าอย่างชัดเจนเพื่อหยุดสัมผัสกับอีกส่วนหรือ voxel
โค้ดตัวอย่างต่อไปนี้แสดงวิธีที่เหตุการณ์ TouchEnded สามารถเชื่อมโยงกับฟังก์ชัน onTouchEnded() ที่กำหนดเอง คล้ายกับ Touched, เหตุการณ์จะส่งอาร์กิวเมนต์ otherPart ไปยังฟังก์ชันเพื่อระบุถึงส่วนอื่นที่เกี่ยวข้อง
การตรวจจับไม่ชนกัน
local Workspace = game:GetService("Workspace")
local part = Workspace.Part
local function onTouchEnded(otherPart)
print(part.Name .. " ไม่ได้สัมผัส " .. otherPart.Name อีกต่อไป)
end
part.TouchEnded:Connect(onTouchEnded)
การกรองการชนกัน
การกรองการชนกัน (filtering) กำหนดว่าส่วนทางกายภาพใดชนกับวัตถุอื่น คุณสามารถกำหนดการกรองสำหรับวัตถุหลายรายการได้ผ่าน กลุ่มการชนกัน หรือคุณสามารถควบคุมการชนตาม ส่วนต่อส่วน ด้วย instances ของ NoCollisionConstraint
กลุ่มการชนกัน
กลุ่มการชนกัน (groups) ช่วยให้คุณระบุ BaseParts ให้กับกลุ่มที่กำหนดและระบุว่ามันจะชนกับกลุ่มอื่น ๆ หรือไม่ ส่วนที่อยู่ในกลุ่มที่ไม่ชนกันจะผ่านกันอย่างเต็มที่แม้ว่าส่วนทั้งสองจะมีค่า CanCollide ตั้งค่าเป็น true
คุณสามารถตั้งค่ากลุ่มการชนกันได้ง่าย ๆ ผ่านตัวแก้ไข กลุ่มการชนกัน ของ Studio ซึ่งสามารถเข้าถึงได้ผ่านเมนู หน้าต่าง ⟩ 3D
ตัวแก้ไขทำงานใน มุมมองรายการ ที่เรียกร้องให้ จอด Dock ที่ด้านซ้ายหรือขวาของ Studio หรือใน มุมมองตาราง ที่กว้างขึ้นซึ่งเรียกร้องให้จอดที่ด้านบนหรือล่าง

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

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

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

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

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

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

สำหรับโค้ดปลั๊กอิน แนะนำให้คุณกำหนด "StudioSelectable" เป็นฟิลเตอร์กลุ่มการชนกันของ RaycastParams ของคุณเมื่อค้นหาส่วนที่อยู่ใต้เคอร์เซอร์ นี่ช่วยให้ปลั๊กอินของคุณตรงกับกลไกการเลือกที่ผู้สร้างได้เรียนรู้ที่จะคาดหวังจากเครื่องมือในตัวของ Studio
การเลือก Raycast ปลั๊กอินที่แนะนำlocal UserInputService = game:GetService("UserInputService")local Workspace = game:GetService("Workspace")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)
การกรองระหว่างส่วน
เพื่อป้องกันไม่ให้เกิดการชนกันระหว่างสองส่วนเฉพาะโดยไม่ต้องตั้งค่ากลุ่มการชนกัน collision groups เช่น ระหว่างล้อของยานพาหนะและตัวถัง คุณสามารถพิจารณาการใช้ การไม่ชนกัน ข้อดีคือ:
- ไม่ต้องใช้กลุ่มการชนกันและ/หรือตัวกำหนดค่า ทำให้คุณสามารถสร้างและแชร์โมเดลด้วยการกรองการชนกันที่ปรับแต่งได้ง่าย
- ส่วนที่เชื่อมต่อกันจะไม่ชนกัน แต่ยังสามารถชนกับวัตถุอื่นได้
ปิดการชนกันของตัวละคร
ตัวละครผู้เล่นของ Roblox จะชนกันโดยค่าเริ่มต้น สิ่งนี้อาจนำไปสู่เกมเพลย์ที่น่าสนใจแต่ไม่ตั้งใจ เช่น ตัวละครกระโดดอยู่บนตัวละครอื่นเพื่อเข้าถึงพื้นที่เฉพาะ หากพฤติกรรมนี้ไม่ต้องการ คุณสามารถป้องกันได้ผ่าน Script ต่อไปนี้ใน ServerScriptService.
สคริปต์ - ปิดการชนกันของตัวละคร
local PhysicsService = game:GetService("PhysicsService")
local Players = game:GetService("Players")
PhysicsService:RegisterCollisionGroup("Characters")
PhysicsService: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
การชนกันของ Mesh และ Solid Model
MeshPart และ PartOperation (ส่วนที่รวมโดย การสร้างรูปทรงแข็ง) เป็นซับคลาสของ BasePart ดังนั้น mesh และส่วนที่ตั้งรูปทรงแข็งจะสืบทอดเหตุการณ์การชนกันเดียวกัน collision events และตัวเลือกการกรองการชนกัน collision filtering เช่นเดียวกับส่วนทั่วไป อย่างไรก็ตาม เนื่องจาก mesh และส่วนที่ตั้งรูปทรงแข็งมักจะมีเรขาคณิตที่ซับซ้อนมากขึ้น พวกเขามีคุณสมบัติ CollisionFidelity ที่โดดเด่นซึ่งกำหนดว่าขอบเขตทางกายภาพจะสอดคล้องกับการแสดงผลภาพสำหรับการจัดการการชนกันอย่างไร
คุณสมบัติ CollisionFidelity มีตัวเลือกต่อไปนี้ โดยเรียงลำดับจากความละเอียดและผลกระทบต่อประสิทธิภาพจากต่ำไปสูง:
- กล่อง — สร้างกล่องการชนที่ถูกจำกัด เหมาะสำหรับวัตถุขนาดเล็กหรือที่ไม่มีการตอบสนอง
- Hull — สร้าง Hull แนวราบ เหมาะสำหรับวัตถุที่มีรอยเว้าและช่องที่สั้น
- ค่าเริ่มต้น — สร้างรูปแบบการชนที่ประมาณการซึ่งรองรับความโค้ง เหมาะสำหรับวัตถุซับซ้อนที่มีความต้องการการตอบสนองรายละเอียดกึ่ง
- PreciseConvexDecomposition — เสนอความละเอียดที่แม่นยำที่สุดแต่ยังไม่สามารถแสดงภาพ 1:1 การเลือกนี้มีค่าใช้จ่ายด้านประสิทธิภาพสูงที่สุดและใช้เวลาในการคำนวณของเอนจินนานขึ้น

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