5.1 KiB
对于碰撞器而言,基本的碰撞器形状,碰撞器的作用不必多谈,这里着重强调的是碰撞器和触发器,以及他们碰撞和触发的三种状态.
Enter Stay Exit
以上三种状态表示了物体碰撞刚刚进入的一帧,物体持续碰撞,和物体离开碰撞的一帧 他们分别对应了代码当中的 OnCollisionEnter OnCollisionStay OnCollisionExit 以及触发器的 OnTriggerEnter OnTriggerStay OnTriggerExit 他们的用法相同,区别在于方法传入的参数不同 碰撞器提供的参数类型是Collision 触发器提供的参数类型是Collider
筛选
我们需要在碰撞器和触发器内部进行许多逻辑上的处理,这里有一个关键是如何筛选物体 比如NPC和玩家同时触发开门触发器,我们只希望玩家触发时才会开门,需要进行筛选. 常见的筛选方法有2种: 签名. 层
签名
由于为我们提供了参数,我们可以获取到触发的物体,所以我们可以使用签名进行筛选
譬如:
if (collision.gameObject.tag==("Player"))
{
}
if (collision.gameObject.CompareTag("Tag"))
{
}
二种方式均可,其中第二种方式的效率更高,因为:
Unity将标签单独储存在一个数据结构当中,CompareTag方法可以直接访问该数据结构效率更高.
层
在Unity当中,层被储存为整数,因此使用层进行筛选的效率比Tag高. 层的筛选也需要分为筛选特定的单层,和多层 比如你需要玩家单独可以开门,那么就可以使用单层筛选 但是如过你想要多个物体都可以开门,那么就需要多层
单层检测
多层检测
1. 关键概念
Layer 和 LayerMask
-
Layer(层): Unity 中的每个
GameObject都可以设置一个Layer,用于分组、筛选或逻辑处理。每个层的编号是从0到31的整数。 -
LayerMask(层掩码):
LayerMask是一个 32 位的整型数,每一位(bit)对应一个 Layer。如果某一位是1,则表示该层被包含在掩码中;如果是0,则表示不包含。例如:
- 如果
LayerMask的值是5(即二进制00000000 00000000 00000000 00000101),表示包含第 0 层和第 2 层。 - 具体映射:
- 第 0 位 = 1 → 包含第 0 层
- 第 1 位 = 0 → 不包含第 1 层
- 第 2 位 = 1 → 包含第 2 层
- 如果
2. 拆解逻辑
(1) collision.gameObject.layer
- 获取发生碰撞的对象的层编号(一个整数,例如
0、1、2等)。
(2) (1 << collision.gameObject.layer)
-
位移操作
<<:1 << x表示将1左移x位。例如:1 << 0→00000000 00000000 00000000 00000001(表示第 0 层)1 << 2→00000000 00000000 00000000 00000100(表示第 2 层)
结果: 这个操作生成一个位掩码,只有对应层的位置是
1,其余位置是0。
(3) targetLayers | (1 << collision.gameObject.layer)
-
按位或操作
|: 将targetLayers和生成的位掩码进行按位或运算。- 如果
targetLayers中已经包含对应的层(对应位置是1),运算结果不变。 - 如果
targetLayers中不包含对应的层(对应位置是0),运算结果会将该位置变为1。
例子:
targetLayers = 00000000 00000000 00000000 00000101(包含第 0 层和第 2 层)collision.gameObject.layer = 11 << 1 = 00000000 00000000 00000000 00000010(表示第 1 层)- 运算结果:
00000000 00000000 00000000 00000111(包含第 0、1 和 2 层)
- 如果
(4) 比较:targetLayers == ...
- 比较
targetLayers和运算结果是否相等。- 如果相等,说明碰撞对象的
Layer已经在targetLayers中。 - 如果不相等,说明碰撞对象的
Layer不在targetLayers中。
- 如果相等,说明碰撞对象的
3. 具体判断逻辑
代码的核心逻辑是:
- 通过
1 << collision.gameObject.layer获取碰撞对象的层对应的位掩码。 - 用
targetLayers | ...检查碰撞对象的层是否在targetLayers中。 - 如果运算结果和
targetLayers相等,说明碰撞对象的层已经被包含。
简化版本: (targetLayers | (1 << collision.gameObject.layer)) 实际上就是在“试探性”地将碰撞对象的层加入 targetLayers,然后检查是否变化。如果没有变化,说明碰撞对象的层已经在 targetLayers 中。
4. 更直观的判断方式
可以使用 LayerMask 的内置方法 LayerMask.Contains(在较新 Unity 版本中),或者稍微改写代码,提升可读性:
替代写法:
csharp
复制代码
if (((1 << collision.gameObject.layer) & targetLayers) != 0) { // 进行内部逻辑 }
逻辑解析:
(1 << collision.gameObject.layer)生成碰撞对象层的位掩码。&(按位与)检测目标层掩码是否包含该层。- 如果结果不为
0,说明目标层掩码包含碰撞对象的层。
- 如果结果不为

