การชนกันเกิดขึ้นเมื่อวัตถุ 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 Workspace = game:GetService("Workspace")
local part = Workspace.Part
local function onTouched(otherPart)
print(part.Name .. " collided with " .. otherPart.Name)
end
part.Touched:Connect(onTouched)โปรดทราบว่าเหตุการณ์ Touched อาจเกิดขึ้นหลายครั้งในระยะเวลาสั้น ๆ ขึ้นอยู่กับการชนกันทางกายภาพที่ละเอียดอ่อน เช่น เมื่อวัตถุที่เคลื่อนที่ "ตั้งอยู่" ในตำแหน่งที่พักหรือเมื่อการชนกันเกี่ยวข้องกับ โมเดลหลายชิ้น เพื่อหลีกเลี่ยงการกระตุ้นเหตุการณ์ Touched มากเกินไป คุณสามารถใช้ระบบดีบาวน์ซ์ง่าย ๆ ที่บังคับใช้ "ระยะเวลาพัก" ผ่าน แอตทริบิวต์ ของอินสแตนซ์
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 .. " collided with " .. 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 Workspace = game:GetService("Workspace")
local part = Workspace.Part
local function onTouchEnded(otherPart)
print(part.Name .. " is no longer touching " .. otherPart.Name)
end
part.TouchEnded:Connect(onTouchEnded)การกรองการชนกัน
การกรองการชนกัน กำหนด ว่าวัตถุทางกายภาพใดชนกับวัตถุอื่น คุณสามารถกำหนดการกรองสำหรับวัตถุจำนวนมากผ่าน กลุ่มการชนกัน หรือคุณสามารถควบคุมการชนกันในระดับ ชิ้นส่วนต่อชิ้นส่วน ด้วยอินสแตนซ์ NoCollisionConstraint
กลุ่มการชนกัน
กลุ่มการชนกัน อนุญาต ให้คุณกำหนด BaseParts ให้กับกลุ่มเฉพาะและระบุว่าพวกมันจะชนกับวัตถุในกลุ่มอื่นหรือไม่ ชิ้นส่วนในกลุ่มที่ไม่ชนกันจะผ่านกันไปโดยสมบูรณ์ แม้ว่าชิ้นส่วนทั้งสองจะมีค่า CanCollide ตั้งเป็น true
คุณสามารถตั้งค่ากลุ่มการชนกันได้อย่างง่ายดายผ่าน Collision Groups ของ Studio ซึ่งสามารถเข้าถึงได้ผ่านเมนู Window ⟩ 3D ของ Studio
ตัวแก้ไขทำงานใน List View ซึ่งเหมาะสำหรับ การจัดเรียง ไปทางด้านซ้ายหรือด้านขวาของ Studio หรือใน Table View ที่กว้างขึ้น ซึ่งเหมาะสำหรับการจัดเรียงไปทางด้านบนหรือล่าง

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

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

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

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

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

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

สำหรับโค้ดปลั๊กอิน แนะนำให้คุณกำหนด "StudioSelectable" เป็นตัวกรองกลุ่มการชนกันของ RaycastParams ของคุณเมื่อค้นหาชิ้นส่วนใต้เคอร์เซอร์ สิ่งนี้ช่วยให้ปลั๊กอินของคุณตรงตามกลไกการเลือกที่ผู้สร้างได้เรียนรู้ที่จะคาดหวังจากเครื่องมือใน Studio ที่สร้างขึ้น
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 เช่น ระหว่างล้อของรถยนต์และตัวถังของมัน ให้พิจารณาใช้ข้อจำกัด No Collision ข้อดีรวมถึง:
- ไม่จำเป็นต้องมีกลุ่มการชนกันและ/หรือสคริปต์การกำหนดค่า ดังนั้นคุณจึงสามารถสร้างและแชร์โมเดลที่มีการกรองการชนกันที่กำหนดเองได้อย่างง่ายดาย
- ชิ้นส่วนที่เชื่อมต่อกันจะไม่ชนกัน แต่ยังสามารถชนกับวัตถุอื่นได้
ปิดการชนกันของตัวละคร
ตัวละครของผู้เล่น 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, "intersected with", otherPart.Name, "| Model parts touching:", numTouchingParts)
end
local function onTouchEnded(otherPart)
-- ละเว้นกรณีที่โมเดลไม่ตัดกันกับตัวเอง
if otherPart:IsDescendantOf(model) then return end
-- ลดจำนวนชิ้นส่วนของโมเดลที่สัมผัส
numTouchingParts -= 1
print(model.Name, "un-intersected from", otherPart.Name, "| Model parts touching:", 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 และชิ้นส่วนที่สร้างโมเดลที่เป็นของแข็งจึงสืบทอด เหตุการณ์การชนกัน และตัวเลือก การกรองการชนกัน เช่นเดียวกับชิ้นส่วนปกติ อย่างไรก็ตาม เนื่องจาก Mesh และชิ้นส่วนที่สร้างโมเดลที่เป็นของแข็งมักมีรูปทรงเรขาคณิตที่ซับซ้อนมากขึ้น พวกมันจึงมีคุณสมบัติ CollisionFidelity ที่โดดเด่นซึ่งกำหนดว่าขอบเขตทางกายภาพจะตรงกับการแสดงผลทางสายตาอย่างไรสำหรับการจัดการการชนกัน
คุณสมบัติ CollisionFidelity มีตัวเลือกดังต่อไปนี้ โดยเรียงตามความแม่นยำและผลกระทบต่อประสิทธิภาพจากต่ำสุดไปสูงสุด:
- Box — สร้างกล่องการชนกันที่เป็นขอบเขต เหมาะสำหรับวัตถุขนาดเล็กหรือไม่โต้ตอบ
- Hull — สร้างฮัลล์นูน เหมาะสำหรับวัตถุที่มีรอยบุ๋มหรือโพรงน้อย
- Default — สร้างรูปร่างการชนกันโดยประมาณที่รองรับความโค้ง เหมาะสำหรับวัตถุที่ซับซ้อนที่มีความต้องการการโต้ตอบที่มีรายละเอียดปานกลาง
- PreciseConvexDecomposition — เสนอความแม่นยำสูงสุดแต่ยังไม่ใช่การแสดงผล 1:1 ของการแสดงผล ตัวเลือกนี้มีค่าใช้จ่ายด้านประสิทธิภาพที่แพงที่สุดและใช้เวลานานกว่าในการคำนวณของเอนจิน

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