碰撞

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

當兩個 3D 物體在 3D 世界中接觸時,就會發生碰撞。為了自定義碰撞處理,BasePart 提供了一組 碰撞事件碰撞過濾 技術,讓您可以控制哪些物理組件與其他組件碰撞。

碰撞事件

碰撞 事件 發生在兩個 BaseParts 在 3D 世界中接觸或停止接觸時。您可以通過 TouchedTouchEnded 事件來檢測這些碰撞,這些事件無論任何一個部件的 CanCollide 屬性值如何都會發生。在考慮部件的碰撞處理時,請注意以下幾點:

Touched

Touched 事件在 BasePart 與另一個部件或 Terrain 像素接觸時觸發。它僅因 物理模擬 而觸發,當部件的 PositionCFrame 被明確設置以使其與另一部件或像素相交時不會觸發。

以下代碼模式顯示了如何將 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 像素的邊界時觸發。它僅因 物理模擬 而觸發,當部件的 PositionCFrame 被明確設置以使其停止與另一部件或像素相交時不會觸發。

以下代碼模式顯示了如何將 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 自動屬於此默認組,除非被指派到其他組,這意味著它們將與 默認 組中的所有其他物體碰撞。

要創建新的碰撞組:

  1. 點擊編輯器面板頂部的 添加組 按鈕,輸入新組名稱,然後按 Enter。新組將出現在列表視圖的兩列中,或在表格視圖的左列和上行中。

    在列表視圖中添加到碰撞組編輯器的新組
  2. 如有必要,重複此過程,為每個組選擇唯一且具描述性的名稱。請注意,您可以通過單擊其字段或選擇它並單擊 重命名 按鈕來在開發過程中更改組的名稱。

    在碰撞組編輯器中指示重命名組的按鈕和字段

配置組碰撞

在默認配置下,所有組中的物體彼此碰撞。要防止一組中的物體與另一組中的物體碰撞,請在相應的行/列中 取消選中 該框。

在以下示例中,Cubes 組中的物體將 Doors 組中的物體碰撞。

在碰撞組編輯器的列表視圖中配置的組

將物體指派到組

要將物體指派到您已 註冊 的組,請通過 Studio 編輯器:

  1. 選擇一個或多個符合碰撞組的 BaseParts

  2. 通過單擊其行的 按鈕將它們指派到該組。物體一次只能屬於一個碰撞組,因此將它們放入新組會將它們從當前組中移除。

    在碰撞組編輯器中指示將選定部件添加到組的加號按鈕

一旦指派,新組將反映在物體的 CollisionGroup 屬性下。

選定的碰撞組顯示為部件的 CollisionGroup 屬性

StudioSelectable 碰撞組

Studio 中的工具使用碰撞過濾系統來確定在 3D 視口中單擊時哪些物體是選擇候選者。指派的碰撞組與 StudioSelectable 不碰撞的物體將被忽略。

例如,如果您在賽車遊戲中有檢查點,其有效區域由大型透明部件定義,您可以將它們指派到 Checkpoints 碰撞組,然後使該組與 StudioSelectable 不可碰撞,以便在編輯底層地圖幾何時不會妨礙。

檢查點組配置為與 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 玩家角色默認會彼此碰撞。這可能導致有趣但意外的遊戲玩法,例如角色跳到彼此身上以到達特定區域。如果這種行為不受歡迎,您可以通過以下 ScriptServerScriptService 中防止它。

腳本 - 禁用角色碰撞
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.TouchedBasePart.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

網格和實體模型碰撞

MeshPartPartOperation(通過 實體建模 連接的部件)是 BasePart 的子類,因此網格和實體建模的部件繼承與常規部件相同的 碰撞事件碰撞過濾 選項。然而,由於網格和實體建模的部件通常具有更複雜的幾何形狀,因此它們具有獨特的 CollisionFidelity 屬性,該屬性決定物理邊界與視覺表示在碰撞處理中的對齊精確度。

CollisionFidelity 屬性具有以下選項,按精度和性能影響從最低到最高排序:

  • 盒子 — 創建一個邊界碰撞盒,適合小型或非互動物體。
  • 外殼 — 生成一個凸包,適合具有不明顯凹陷或空腔的物體。
  • 默認 — 生成一個近似的碰撞形狀,支持凹陷,適合具有半詳細交互需求的複雜物體。
  • 精確凸分解 — 提供最精確的精度,但仍然不是視覺的 1:1 表示。此選項的性能成本最高,並且需要更長的時間讓引擎計算。
城堡塔的原始網格

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

©2026 Roblox Corporation、Roblox、Roblox 標誌及 Powering Imagination 是我們在美國及其他國家地區的部分註冊與未註冊商標。