当两个 3D 对象在 3D 世界中发生接触时,就会发生碰撞。对于自定义碰撞处理,BasePart 有一组 碰撞事件 和 碰撞过滤 技巧,因此您可以控制哪些物理装配会与其他装配碰撞。
碰撞事件
当两个 BaseParts 在 3D 世界中触摸或停止触摸时,发生碰撞事件 **** 您可以通过 Touched 和 TouchEnded 发生的事件检测这些碰撞,无论哪一方的 CanCollide 属性值是否相同。当考虑零件的碰撞处理时,请注意以下几点:
- 零件的 CanCollide 属性影响其是否会 物理 碰撞其他零件并导致力量对它们起效。即使在某个部分禁用了 CanCollide,你也可以通过 Touched 和 TouchEnded 事件检测到触碰和非触碰。
- 和 事件只在物理移动的结果下发生,而不是从 物理移动 或 物理变更 导致零件相交或停止相交的零件。
已触摸
当 Touched 事件发生时,一个 BasePart 与另一个或与一个 地形 体接触或交互时,它会触发。它仅在物理模拟的结果下发射,不会在零件的或被明确设置为与另一部分或体素交叉时发射。
以下代码模式显示了如何将 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事件,你可以通过实现一个简单的缓冲系统来强制一个“冷却”期间通过实例属性属性。
部件碰撞与冷却时间
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) -- 将属性设置为真值
task.wait(COOLDOWN_TIME) -- 等待冷却时间
part:SetAttribute("Touched", false) -- 重置属性
end
end
part.Touched:Connect(onTouched)
触摸已结束
当整个碰撞边界的 事件退出另一个 或已填充的 地形时,就会触发事件。它只会因为 物理模拟 而发射,不会在零件的 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 。
您可以通过 Studio 的 碰撞集群编辑器 轻松设置碰撞集群,可通过单击工具栏的 碰撞集群 按钮访问。

编辑器在 列表视图 中运行,这有利于将 对齐 向 Studio 左侧或右侧移动,或在更广的 表视图 中运行,这有利于将对齐移至 Studio 顶部或底部。

注册群组
编辑器包含一个默认 冲突集 ,无法重命名或删除。所有 BaseParts 默认属于此默认组,除非被分配到另一个群组,这意味着它们将与默认组 默认群组 中的所有其他对象发生冲突。
要创建一个新的碰撞群组:
单击编辑器面板顶部的 添加组 按钮,输入新的组名称,然后按 Enter 。新组出现在列表视查看的两个列中,或在表单视查看的左栏和顶行中。
如果需要,重复该过程,为每个群组选择独特且描述性的名称。请注意,您可以在开发期间更改群组的名称,单击其字段,或选择它并单击 重命名 按钮。
配置群组碰撞
在默认配置下,所有群组中的对象相互碰撞。为了防止一个组中的对象与另一个组中的对象发生碰撞, 卸下 相应行/列中的复选框。
在以下示例中,立方体组的对象不会与门群组的对象碰撞。

将对象分配到群组
要将对象分配到您通过 Studio 编辑器注册的组:
选择一个或多个BaseParts,可以作为碰撞群组的一部分。
通过单击其行的 ⊕ 按钮将它们分配给组。对象只能属于一个碰撞组,一次,因此将它们放置在新组中会将它们从当前群组中移除。
一旦分配,新组就会反映在对象的 CollisionGroup 属性下。

工作室可选碰撞群组
工作室中的工具使用碰撞过滤系统来确定哪些对象是候选对象,当单击 3D 视图时。其分配的碰撞组不与 StudioSelectable 碰撞的对象将被忽略。
例如,如果你有一个竞速体验中的检查点 whose有效区域由大型透明部件定义,你可以将它们分配到一个 检查点 碰撞集群,然后使该集群与 StudioSelectable 不可碰撞,以便它们在编辑基础地图几何时不会挡道。

对于插件验证码,建议您在寻找鼠标下的零件时将 指定为冲突组过滤器,当在鼠标下找到零件时。这可以让你的插件与创建者习惯的内置 Studio 工具的选择机制匹配。
推荐的插件选择射线投射
local UserInputService = game:GetService("UserInputService")local Workspace = game:GetService("Workspace")local raycastParams = RaycastParams.new()raycastParams.CollisionGroup = "StudioSelectable" -- 遵循惯例raycastParams.BruteForceAllSlow = true -- 以便选择带有"false"的CanQuery的零件local mouseLocation = UserInputService:GetMouseLocation()local mouseRay = Workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)local filteredSelectionHit = Workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 10000, raycastParams)
零件到零件过滤
为了防止两个特定部件之间的碰撞,而不设置碰撞组,例如车轮与底盘之间,请考虑没有碰撞约束。优势包括:
- 不需要碰撞组和/或配置脚本,因此您可以轻松创建并共享具有自定义碰撞过滤的模型。
- 连接的零件不会相互碰撞,但它们仍然可以碰撞其他对象。
禁用角色碰撞
Roblox 玩家角色默认会相互碰撞。这可能会导致有趣但不意味着的游戏玩法,例如角色互相跳跃以到达特定区域。如果这种行为不合适,您可以通过以下 Script 在 ServerScriptService 中禁止它。
脚本 - 禁用角色碰撞
local PhysicsService = game:GetService("PhysicsService")
local Players = game:GetService("Players")
local CollisionGroupName = "Characters"
PhysicsService:RegisterCollisionGroup(CollisionGroupName)
PhysicsService:CollisionGroupSetCollidable(CollisionGroupName, CollisionGroupName, false)
local function setCollisionGroup(model)
-- 将碰撞组应用到模型中的所有现有零件
for _, descendant in model:GetDescendants() do
if descendant:IsA("BasePart") then
descendant.CollisionGroup = CollisionGroupName
end
end
end
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
setCollisionGroup(character)
end)
-- 如果玩家已经有角色,立即应用冲突组
if player.Character then
setCollisionGroup(player.Character)
end
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
网格和固体模型碰撞
MeshPart 和 PartOperation (由 固体建模 连接的零件)是 BasePart 的子类,因此网格和固体模型的零件继承了与普通零件相同的 碰撞事件 和 碰撞过滤器 选项。然而,由于网格和实体模型部件通常具有更复杂的几何图形,它们通常具有一个区分性的 CollisionFidelity 属性,决定物理边界与视觉表示的碰撞处理精确度。
CollisionFidelity 属性有以下选项,从低到高的稳定性和性能影响顺序:
- 箱子 — 创建一个封装碰撞方块,非常适合小或非互动对象。
- 船体 — 生成凸弧船体,适合具有较淡的凹陷或空洞的对象
- 默认 — 生成一个大约相同的碰撞形状,支持凹陷,适合有半详细互动需求的复杂对象。
- 精确凸起分解 — 提供最精确的忠实度,但仍不是视觉的 1:1 表示。该选项的性能成本最高,需要更长时间才能计算出引擎。

了解有关碰撞准确度选项的性能影响以及如何应对它们的更多信息,请参阅性能优化。