Unity3D 中如何通过位运算来开启和关闭指定层

在 Unity3D 开发的过程中,我们经常会有选取指定层的需求,例如设定某个摄像机只渲染某几个层的对象,UI 摄像机通常就只选取 UI 所在的层进行渲染,而对于其他层的对象进行忽略;场景摄像机通常会对非 UI 层的所有对象进行渲染,那么除了在 Unity3D Editor 中对场景中已有的 GameObject 直接进行设置之外,我们还有更为灵活的方法吗?当然有了,只要通过简单的位运算就可以把这个需求满足了。

我们先用摄像机来举例吧,Camera 的 cullingMask 属性就是用来设置摄像机渲染层用的,例如我们想设置名为 mainCamera 的摄像机的渲染层为 UI 层和 PopUI 层,那么代码应该如下:

mainCamera.cullingMask = LayerMask.GetMask ("UI", "PopUI");

上面的这段代码非常容易理解,如果我们的需求就是针对有限的少数几个层进行设置的话,这种方法是最好用的。但是,我们通常都会有一些针对特定层进行动态设置的需求,例如我们需要动态地将 PopUI 层从 mainCamera 的 cullingMask 中剔除掉,那么我们应该怎么做呢?可以简单地依照上方的代码来做:

mainCamera.cullingMask = LayerMask.GetMask ("UI");

这样确实能满足我们的需求,但是这是在我们已经完全知道 mainCamera 具体需要渲染哪些层的前提下才可行的。例如我们有一个名为 fightCamera 的摄像机,其对非 UI 和 PopUI 层之外的所有层进行渲染,由于游戏中设计的层数较多,其渲染的层总数可能达到 20 个,那么此时使用上面的这种方式来关闭某个层那么就显得非常的蛋疼了,我们总不能把那 20 层的名称都写到代码里头吧,咱们暂且不说可能会写错,这看上去就不是个聪明的办法啊。

这个时候,我们就需要使用到位运算了,例如我们想确保名为 FX 的层在 fightCamera 的 cullingMask 中,那么我们只需要使用下面的代码就可以了:

int layerFXMask = LayerMask.GetMask ("UI");
// 这段代码和上面的代码功能是一样滴
// int layerFXMask = 1 << LayerMask.NameToLayer ("UI");
fightCamera.cullingMask |= layerFXMask;

如果想把 FX 所处的层给关闭了呢,那么使用下面的代码就行了:

int layerFXMask = LayerMask.GetMask ("UI");
// int layerFXMask = 1 << LayerMask.NameToLayer ("UI");
fightCamera.cullingMask &= ~layerFXMask;

如果是想开/关,这样来回切换 FX 所在层的开启状态,那么使用下面的代码就行了:

int layerFXMask = LayerMask.GetMask ("UI");
// int layerFXMask = 1 << LayerMask.NameToLayer ("UI");
fightCamera.cullingMask ^= layerFXMask;

以上这三种情况,分别为开启、关闭和开/关这三种状态,对应的是位运算的按位或、按位与(取反)和按位异或操作。

以上三种情况中最终得到的 cullingMask 值实际上就是一个 int 类型的数字,该方式适用于 Unity3D 中所有接受 LayerMask 作为参数的方法,例如 Physics.Raycast 方法:

public static bool Raycast(Vector3 origin, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers);

这个方法最后的参数就可以传入我们通过位运算获得的 LayerMask 值,用于指定我们需要进行碰撞检测的层。