當兩個 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 屬性具有以下選項,按精度和性能影響從最低到最高排序:
- 盒子 — 創建一個邊界碰撞盒,適合小型或非互動物體。
- 外殼 — 生成一個凸包,適合具有不明顯凹陷或空腔的物體。
- 默認 — 生成一個近似的碰撞形狀,支持凹陷,適合具有半詳細交互需求的複雜物體。
- 精確凸分解 — 提供最精確的精度,但仍然不是視覺的 1:1 表示。此選項的性能成本最高,並且需要更長的時間讓引擎計算。

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