碰撞

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

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

碰撞事件

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

Touched

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

以下代碼模式顯示了如何將 Touched 事件連接到自定義的 onTouched() 函數。請注意,該事件將 otherPart 參數發送到函數,指示參與碰撞的另一部件。

部件碰撞
local part = workspace.Part
local function onTouched(otherPart)
print(part.Name .. " 與 " .. otherPart.Name .. " 碰撞")
end
part.Touched:Connect(onTouched)

請注意,Touched 事件可能會因微妙的物理碰撞而快速多次觸發,例如當一個移動物體“穩定”到靜止位置時,或當碰撞涉及 多部件模型 時。為了避免觸發過多的 Touched 事件,您可以實現一個簡單的防抖系統,通過實例 屬性 強制執行“冷卻”時間。

帶冷卻的部件碰撞
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 或填充的 Terrain 像素的邊界時,TouchEnded 事件會觸發。它僅因 物理模擬 而觸發,當部件的 PositionCFrame 被明確設置為使其停止與另一部件或像素相交時,則不會觸發。

以下代碼模式顯示了如何將 TouchEnded 事件連接到自定義的 onTouchEnded() 函數。與 Touched 一樣,該事件將 otherPart 參數發送到函數,指示參與碰撞的另一部件。

非碰撞檢測
local part = workspace.Part
local function onTouchEnded(otherPart)
print(part.Name .. " 不再接觸 " .. otherPart.Name)
end
part.TouchEnded:Connect(onTouchEnded)

碰撞過濾

碰撞 過濾 定義哪些物理部件與其他部件碰撞。您可以通過 碰撞組 配置多個物體的過濾,或者您可以使用 NoCollisionConstraint 實例在 部件到部件 的基礎上控制碰撞。

碰撞組

碰撞 讓您將 BaseParts 指派到專用組,並指定它們是否與其他組中的物體碰撞。位於不碰撞組中的部件會完全穿過彼此,即使兩個部件的 CanCollide 屬性都設置為 true。碰撞組是在 WorkspaceWorldModel 的基礎上創建和配置的。

在上面的視頻中,旋轉的物體位於不同的碰撞組中,因此它們與其他顏色的物體碰撞,但不與自己顏色的物體碰撞

您可以通過 Studio 的 碰撞組 編輯器輕鬆設置碰撞組,該編輯器可以通過 Studio 的 窗口 ⟩ 3D 菜單訪問。

註冊組

Workspace 存儲其自己的可配置碰撞組狀態。此外,任何 WorldModel 都可以選擇使用自己的碰撞組進行查詢。

編輯器包括一個 默認 碰撞組,無法重命名或刪除。所有 BaseParts 自動屬於此默認組,除非被指派到其他組,這意味著它們將與 默認 組中的所有其他物體碰撞。

要創建新的碰撞組:

  1. 默認情況下,組在 Workspace 上註冊,但如果您的地方中有至少一個 WorldModel,則在碰撞組編輯器的頂部會出現選擇下拉菜單。使用下拉菜單在不同的 WorldModel 實例之間配置碰撞組。

    顯示為碰撞組編輯器當前活動世界的工作區

    對於任何 WorldModel,您可以啟用 使用工作區碰撞組 以使其使用 Workspace 碰撞組進行查詢。

    顯示為切換 WorldModel 的 UseWorkspaceCollisionGroups 屬性的復選框
  2. 點擊編輯器面板頂部的 添加組 按鈕,輸入新組名稱,然後按 Enter。新組將出現在列表視圖的兩列中,或在表格視圖的左列和上行中。

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

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

配置組碰撞

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

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

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

將物體指派到組

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

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

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

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

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

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

StudioSelectable 組

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

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

檢查點組配置為與 StudioSelectable 組不可碰撞

對於插件代碼,建議您在查找光標下的部件時,將 "StudioSelectable" 指派為您的 RaycastParams 的碰撞組過濾器。這使您的插件能夠匹配創作者從內置 Studio 工具中學到的選擇機制。

推薦的插件選擇射線
local UserInputService = game:GetService("UserInputService")
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 Players = game:GetService("Players")
workspace:RegisterCollisionGroup("Characters")
workspace: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 屬性具有以下選項,按精度和性能影響從最低到最高排序:

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

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

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