冲突

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

当两个 3D 对象在 3D 世界中发生接触时,就会发生碰撞。对于自定义碰撞处理,BasePart 有一组 碰撞事件碰撞过滤 技巧,因此您可以控制哪些物理装配会与其他装配碰撞。

碰撞事件

当两个 BaseParts 在 3D 世界中触摸或停止触摸时,发生碰撞事件 **** 您可以通过 TouchedTouchEnded 发生的事件检测这些碰撞,无论哪一方的 CanCollide 属性值是否相同。当考虑零件的碰撞处理时,请注意以下几点:

  • 零件的 CanTouch 属性决定是否触发碰撞事件。如果设置为 false ,那么 TouchedTouchEnded 都不会触发。
  • 零件的 CanCollide 属性影响其是否会 物理 碰撞其他零件并导致力量对它们起效。即使在某个部分禁用了 CanCollide,你也可以通过 TouchedTouchEnded 事件检测到触碰和非触碰。
  • 和 事件只在物理移动的结果下发生,而不是从 物理移动 或 物理变更 导致零件相交或停止相交的零件。
  • 顶层 Terrain 类从 BasePart 继承,因此您可以将 碰撞组 分配给 Terrain 以确定其他 BaseParts 是否与 地形 体積碰撞。

已触摸

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)

触摸已结束

当整个碰撞边界的 事件退出另一个 或已填充的 地形时,就会触发事件。它只会因为 物理模拟 而发射,不会在零件的 PositionCFrame 被明确设置为停止交叉另一部分或体素时发射

以下代码模式显示了如何将 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 的 碰撞集群编辑器 轻松设置碰撞集群,可通过单击工具栏的 碰撞集群 按钮访问。

Collision Groups tool indicated in Model tab of Studio

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

List View example in Collision Groups Editor

注册群组

编辑器包含一个默认 冲突集 ,无法重命名或删除。所有 BaseParts 默认属于此默认组,除非被分配到另一个群组,这意味着它们将与默认组 默认群组 中的所有其他对象发生冲突。

要创建一个新的碰撞群组:

  1. 单击编辑器面板顶部的 添加组 按钮,输入新的组名称,然后按 Enter 。新组出现在列表视查看的两个列中,或在表单视查看的左栏和顶行中。

    New group added to Collision Groups Editor in List View
  2. 如果需要,重复该过程,为每个群组选择独特且描述性的名称。请注意,您可以在开发期间更改群组的名称,单击其字段,或选择它并单击 重命名 按钮。

    Button and field indicated for renaming a group in the Collision Groups Editor

配置群组碰撞

在默认配置下,所有群组中的对象相互碰撞。为了防止一个组中的对象与另一个组中的对象发生碰撞, 卸下 相应行/列中的复选框。

在以下示例中,立方体组的对象不会与门群组的对象碰撞。

Group configured in List View of Collision Groups Editor

将对象分配到群组

要将对象分配到您通过 Studio 编辑器注册的组:

  1. 选择一个或多个BaseParts,可以作为碰撞群组的一部分。

  2. 通过单击其行的 按钮将它们分配给组。对象只能属于一个碰撞组,一次,因此将它们放置在新组中会将它们从当前群组中移除。

    Plus button indicated in Collision Groups Editor for adding selected parts to a group

一旦分配,新组就会反映在对象的 CollisionGroup 属性下。

Chosen collision group indicated as the part's CollisionGroup property

工作室可选碰撞群组

工作室中的工具使用碰撞过滤系统来确定哪些对象是候选对象,当单击 3D 视图时。其分配的碰撞组不与 StudioSelectable 碰撞的对象将被忽略。

例如,如果你有一个竞速体验中的检查点 whose有效区域由大型透明部件定义,你可以将它们分配到一个 检查点 碰撞集群,然后使该集群与 StudioSelectable 不可碰撞,以便它们在编辑基础地图几何时不会挡道。

Checkpoints group configured to be non-collidable with StudioSelectable group

对于插件验证码,建议您在寻找鼠标下的零件时将 指定为冲突组过滤器,当在鼠标下找到零件时。这可以让你的插件与创建者习惯的内置 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 玩家角色默认会相互碰撞。这可能会导致有趣但不意味着的游戏玩法,例如角色互相跳跃以到达特定区域。如果这种行为不合适,您可以通过以下 ScriptServerScriptService 中禁止它。

脚本 - 禁用角色碰撞

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.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, "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

网格和固体模型碰撞

MeshPartPartOperation (由 固体建模 连接的零件)是 BasePart 的子类,因此网格和固体模型的零件继承了与普通零件相同的 碰撞事件碰撞过滤器 选项。然而,由于网格和实体模型部件通常具有更复杂的几何图形,它们通常具有一个区分性的 CollisionFidelity 属性,决定物理边界与视觉表示的碰撞处理精确度。

CollisionFidelity 属性有以下选项,从低到高的稳定性和性能影响顺序:

  • 箱子 — 创建一个封装碰撞方块,非常适合小或非互动对象。
  • 船体 — 生成凸弧船体,适合具有较淡的凹陷或空洞的对象
  • 默认 — 生成一个大约相同的碰撞形状,支持凹陷,适合有半详细互动需求的复杂对象。
  • 精确凸起分解 — 提供最精确的忠实度,但仍不是视觉的 1:1 表示。该选项的性能成本最高,需要更长时间才能计算出引擎。
Original mesh of castle tower

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