碰撞發生在兩個 3D 物體在 3D 世界中接觸時。為了自定義碰撞處理,BasePart 提供了一組 碰撞事件 和 碰撞過濾 技術,您可以控制哪些物理組件與其他組件發生碰撞。
碰撞事件
碰撞 事件 發生在兩個 BaseParts 在 3D 世界中接觸或停止接觸時。您可以通過 Touched 和 TouchEnded 事件來檢測這些碰撞,這些事件會在任何一部分的 CanCollide 屬性值下觸發。在考慮部分的碰撞處理時,請注意以下幾點:
- 一個部分的 CanCollide 屬性影響它是否會與其他部分 物理 碰撞並導致力量作用於它們。即使對於一個部分禁用 CanCollide,仍然可以通過 Touched 和 TouchEnded 事件來檢測接觸和非接觸。
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 .. " 與 " .. 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 .. " 與 " .. 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 .. " 不再接觸 " .. otherPart.Name)
end
part.TouchEnded:Connect(onTouchEnded)
碰撞過濾
碰撞 過濾 定義了哪些物理部分與其他部分發生碰撞。您可以通過 碰撞組 配置多個物件的過濾,或使用 NoCollisionConstraint 實例控制 部件對部件 的碰撞。
碰撞組
碰撞 組 讓您將 BaseParts 分配給專用組,並指定它們是否與其他組中的物件發生碰撞。位於不發生碰撞組中的部分會完全穿透彼此,即使兩個部分的 CanCollide 屬性設置為 true。
您可以輕鬆通過 Studio 的 碰撞組 編輯器來設置碰撞組,該編輯器可以通過 Studio 的 窗口 ⟩ 3D 菜單訪問。
編輯器支援 列表視圖,更適合 停靠 在 Studio 的左側或右側,或支援更寬的 表格視圖,更適合停靠在頂部或底部。

註冊組
編輯器包括一個 默認 碰撞組,該組無法重新命名或刪除。所有 BaseParts 默認屬於此組,除非分配給其他組,這意味著它們將與 默認 組中的所有其他物件發生碰撞。
要創建新的碰撞組:
點擊編輯器面板頂部的 添加組 按鈕,輸入新組名稱並按 Enter。新組顯示在列表視圖的兩個列中,或在表格視圖的左列和上行中。

如有必要,重複該過程,為每個組選擇唯一且具描述性的名稱。請注意,您可以通過單擊其字段來變更組的名稱,或通過選擇它並單擊 重新命名 按鈕進行更改。

配置組碰撞
在默認配置下,所有組中的物件相互之間會發生碰撞。要防止一組中的物件與其他組中的物件發生碰撞,取消選中 相應行/列中的方框。
在以下示例中,Cubes 組中的物件將 不 與 Doors 組中的物件發生碰撞。

將物件指派給組
要通過 Studio 編輯器將物件指派給您 註冊的 碰撞組:
選擇一個或多個符合碰撞組條件的 BaseParts。
通過單擊其行的 ⊕ 按鈕將它們分配給該組。物件一次只能屬於一個碰撞組,因此將它們放入新組中會將它們從當前組中移除。

一旦分配,新的組會在物件的 CollisionGroup 屬性下顯示。

StudioSelectable 碰撞組
Studio 中的工具使用碰撞過濾系統來確定在 3D 檢視區中單擊時哪些物件是可選的。指派的碰撞組不與 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)
部件對部件過濾
要防止兩個特定部件之間的碰撞而不設置 碰撞組,例如車輛的輪子和車身之間的碰撞,請考慮使用 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, "與", 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 屬性具有以下選項,根據精確度與性能影響從低到高排序:
- Box — 創建包圍碰撞框,適合於小型或非交互式物體。
- Hull — 生成一個凸包,適合於邊緣不明顯的物體。
- Default — 生成近似的碰撞形狀,支持凹面,適合於具有半詳細交互需求的複雜物體。
- PreciseConvexDecomposition — 提供最精確的精度,但仍不是視覺的 1:1 表現。此選項的性能成本最高,並且更耗時。

有關碰撞精度選項的性能影響及如何減少其影響的更多信息,請參見 性能優化。