文章目录
- 层(Layer)
- 什么是Layer
- Layer的应用场景
- Layer层的配置(Tags & Layers)
- Layer的数据结构
- LayerMask
- Layer的选中和忽略
- Layer的管理(架构思路)
- 层碰撞矩阵设置(Layer Collision Matrix)
- 层排序(Sorting Layers)
- 设置2D物体的Sorting Layer
- 设置2D物体的Order In Layer
- 标签(Tag)
- Tag标签的配置
- 给物体设置标签
- Tag的基本用法
- 获取场景中的物体
- 判断物体是否具有某个Tag
- Tag标签的应用场景
层(Layer)
什么是Layer
在Unity中,Layer的核心作用其实就是给游戏对象分类,这样我们就可以针对这些类别进行统一处理。比如我正在做一个Moba游戏,需要一个小地图,我打算用一个相机从地图上方俯视,这时候我需要在地图中显示一些标记(比如玩家的标记,怪物的标记)在地图上,这些标记是跟着玩家移动的,但是我的主相机中又不想看到这些标记,这时候就可以把这些标记指定为一个特殊的层,比如 “MapSignal” ,然后我们将主相机设置为不看 “MapSignal” 层,我们的功能就实现了。反之,我们可以在地图相机上,将玩家的Layer去掉,只保留 “MapSignal” 层,这样地图相机就不会显示玩家模型了。
Layer的应用场景
刚才举的例子只是 Layer 的一种用法,实际上 Layer 还有很多种作用,具体如下:
- 在 Scene 视图中隐藏某些 Layer ,可以在Scene视图工具栏中选择Layers,通过设置哪些Layer将出现在Scene视图中,以及哪些Laver不会出现;
- 排除不被灯光照亮的 Layer ,在创建自定义的用户界面、阴影系统或者使用复杂的光照系统时,可以选择灯光对象,然后在对应的Inspector视图中点击Culling Mask属性,取消选择你想忽略的那些Layer;
- 用于物理交互。在 Edit => ProiectSetings => Physics 菜单中,可以找到Layer Colision Matrix属性,用它来设置哪些游戏对象之间可以进行物理交互;
- 用于射线检测的 Layer 。在某些情况下,可以使用 Layer 来忽略一些 Collider或Collision,比如在做射线检测时;
- 通过名称获取 Layer ,并 “开启 / 关闭” Layer ,可以通过 LayerMask.NameToLayer 方法获取特定的 Layer ,例如:
int layer = LaverMask NameToLayer("Enemy");
,然后可以使用1左移位运算符开启并获取Enemy Layer层,或者使用0左移位运算符关闭并获取Enemy Layer层。
Layer层的配置(Tags & Layers)
Unity中有一个用于设置标签(Tag)和层(Layer)的面板叫做 “Tags & Layers” ,打开方式很简单,只要选定场景中的任意对象,然后在Inspector面板中点击 Layer => Add Layer 即可。也可以在主菜单中点击 Edit => Project Settings,然后选择 Tags and Layers 标签页。
Layer的数据结构
Layer 数据其实是一个 int 值,每个 Layer 都是一个 int 值,且该值是 2 的 N 次方,看到这里相信有经验的人都知道是二进制的计算方法,由于 int32 是一个 32 位的二进制数,所以 Unity 的层也就设置了 32 个(0 ~ 31),所以每个层对应的数值就是 2 的 0 ~ N 次方,比如第 0 个 Layer 对应的值是 1,第 8 个 Layer 对应的值是 256 ,第十个 Layer 则是 1024。那么假如我给定一个数是 257 ,就代表了我同时拥有第 0 层( 2的0次方 = 1 )和第 8 层( 2的8次方 = 256 ),如下图,Layer0(Default)对应的int值就是2的0次方 = 1,而Layer7(Player)对应的就是2的7次方 = 128。当我们想要同时指定多个层时,就可以使用一个int值来表达。
LayerMask
LayerMask可以用来指定一个范围,以帮助检测物体是否在范围内,或设置射线是否会打到某个范围的层上。以射线为例:
// 指定第八层和第九层
int layers = (1 << 8) | (1 << 9);
// 入射射线
var ray = new Ray(startPoint, inDircection);
// 射线只会检测第八层和第九层的物体
var hits = new List<RaycastHit>(Physics.RaycastAll(ray, distance, layers));
在上面的案例中,射线只会检测第八层和第九层的物体,这样不但提高了运行效率,还使代码逻辑更为简洁,不需要手动判断物体类型。
Layer的选中和忽略
在代码中使用 | 可以使多个层联系在一起,使用 ~ 则会将指定的层排除掉,如下图:
Layer的管理(架构思路)
由于不确定在什么样的时候有可能会修改Layer的名称,所以不建议在代码中直接写入大量的Layer名称,所以通常要搞一个层的维护类,将层的名字做成常量,并提供更便捷的访问方式,代码如下:
using UnityEngine;
namespace Battle.Logic.LayerMasks
{
/// <summary>
/// 层管理器
/// 所有层Layer相关的操作都应该在此类中完成
/// <para>
/// <Author>开发者 : Genesis (*╹▽╹*) </Author>
/// </para>
/// </summary>
public static class LayerMaskManager
{
/// <summary>
/// 地面层
/// </summary>
private const string LayerGround = "Ground";
/// <summary>
/// 可触发的草
/// 玩家进入草丛后可以隐身
/// </summary>
private const string LayerTriggerGrass = "TriggerGrass";
/// <summary>
/// 可触发的水
/// 玩家进入水后可能被减速甚至被淹死
/// </summary>
private const string LayerTriggerWater = "TriggerWater";
/// <summary>
/// 可触发层
/// 通常是有触发器的碰撞体才会在这一层
/// </summary>
private const string LayerCanTrigger = "CanTrigger";
/// <summary>
/// 可碰撞层
/// 通常是可以影响玩家位置的碰撞体才会在这一层
/// </summary>
private const string LayerCanCollide = "CanCollide";
/// <summary>
/// 逻辑玩家层
/// </summary>
private const string LayerLogicPlayer = "LogicPlayer";
/// <summary>
/// 显示玩家层
/// </summary>
private const string LayerDisplayPlayer = "DisplayPlayer";
/// <summary>
/// 玩家是否接触地面的层遮罩
/// </summary>
public static LayerMask PlayerOnGroundLayerMask = LayerMask.GetMask(layerNames: new[] { LayerGround, LayerCanCollide });
/// <summary>
/// 玩家投棉球瞄准线的Layer遮罩
/// </summary>
public static LayerMask PlayerAimLayerMask = LayerMask.GetMask(layerNames: new[] { LayerGround, LayerCanCollide });
/// <summary>
/// 玩家投棉球子弹的Layer遮罩
/// </summary>
public static LayerMask CottonBulletLayerMask = LayerMask.GetMask(layerNames: new[] { LayerGround, LayerCanCollide });
/// <summary>
/// 摆放炮台环节显示的Layer遮罩
/// </summary>
public static LayerMask DefensePutLayerMask =
LayerMask.GetMask(layerNames: new[] { LayerGround, LayerTriggerGrass, LayerTriggerWater, LayerCanCollide });
/// <summary>
/// 判断obj是否在layerMask中
/// </summary>
/// <param name="obj">需要验证的物体</param>
/// <param name="layerMask">指定的层</param>
/// <returns>物体是否在指定的层中</returns>
public static bool IsInLayerMask(GameObject obj, LayerMask layerMask)
{
// 根据Layer数值进行移位获得用于运算的Mask值
var objLayerMask = 1 << obj.layer;
return (layerMask.value & objLayerMask) > 0;
}
/// <summary>
/// 判断objMask是否在layerMask中
/// </summary>
/// <param name="objMask">需要验证的物体的层</param>
/// <param name="layerMask">指定的层</param>
/// <returns>该物体的层是否在指定的层中</returns>
public static bool IsInLayerMask(LayerMask objMask, LayerMask layerMask)
{
// 根据Layer数值进行移位获得用于运算的Mask值
var objLayerMask = 1 << objMask;
return (layerMask.value & objLayerMask) > 0;
}
}
}
在上面的代码中,我将项目中层的名称定义为常量,后续使用层的名称时只允许使用常量,不允许直接使用字符串形式的LayerName,这样一旦层的名称有改动只需要改动此常量即可。
然后又提供了一些常用的已经绑定好的LayerMask(例如:PlayerAimLayerMask),便于在代码中直接使用,使用代码如下:
// 入射射线
var ray = new Ray(startPoint, inDircection);
// 获取射线经过的点(这里对层进行了过滤)
var hits = new List<RaycastHit>(Physics.RaycastAll(ray, distance, LayerMaskManager.PlayerAimLayerMask));
层碰撞矩阵设置(Layer Collision Matrix)
Unity支持通过设置决定层与层之间的碰撞关系,如下图:
物理引擎在检测碰撞时会通过Layer Collision Matrix的设置内容来决定要针对哪些层做碰撞监测,勾选的层之间碰撞有监测,没有勾选的层之间完全不会产生任何碰撞。
层排序(Sorting Layers)
Sorting Layers的主要作用是用于改变2D物体的渲染顺序,在 Hierarchy 中创建的 Canvas,Unity 默认自上而下渲染,位置越靠后的 Canvas 将渲染在靠前位置的 Canvas 之上,但有时我们需要让位置后靠后的Canvas先进行渲染或者让位置靠前的Canvas后渲染,此时就要用到Sorting Layer。
如上图,越往上的层级渲染顺序越靠前,也就是说下面层级的物体会显示在更上面一些。
设置2D物体的Sorting Layer
在Sprite Renderer组件中有一个Additional Settings节点,下面可以设置与层相关的功能。想要物体显示的靠前,就设置相对较高的层级即可。
设置2D物体的Order In Layer
有时候在同一Sorting Layer下也要区分物体的渲染顺序,这时候就需要用到Order In Layer设置,跟Sorting Layer差不多,数值越大,渲染越靠前。
标签(Tag)
由于Layer的数据结构相对简单,类似简单的树状结构,在某些复杂条件下可能无法满足开发需求。所以,Unity在层之外还做了一个Tag(标签)功能,每个物体都可以设置相应的Layer和Tag,通过Tag就将简单的树形结构改成了网状结构,例如:同一个层的物体可以又有标签A又有标签B,这样做的好处是可以实现跨层对某类物体进行判定筛选。
标签有助于识别游戏对象以便于编写脚本。通过使用标签,不需要使用拖放方式手动将游戏对象添加到脚本的公开属性,因此可以节省在多个游戏对象中使用相同脚本代码的时间。
Tag标签的配置
Tag标签的配置和Layer在同一个功能页中,面板图示如下:
最上面的Tags就是用于设置标签的。
给物体设置标签
选中物体后,Inspector面板左上角会有一个Tag下拉菜单,可以用来设置物体的Tag。
Tag的基本用法
获取场景中的物体
Unity支持通过标签获取场景中的物体,代码如下:
// 获取场景中的标签为MyTag01的所有游戏对象
GameObject.FindGameObjectsWithTag("MyTag01")
// 获取具有MyTag01标签的第一个GameObject
GameObject.FindWithTag("MyTag01");
我们可以通过这种方式获取场景中的某一类别的物体,且不受层级Layer影响。
判断物体是否具有某个Tag
判断物体是否具有某个Tag可以使用**gameObject.CompareTag(string)**函数,代码如下:
void Update()
{
// 假设你想检查这个GameObject是否有Tag为"Player"
if (gameObject.CompareTag("Player"))
{
// 如果是玩家,执行相关逻辑
Debug.Log("This is the player.");
}
}
Tag标签的应用场景
标签 (Tag) 是可分配给一个或多个游戏对象的参考词。例如,可为玩家控制的角色定义“Player”标签,并为非玩家控制的角色定义“Enemy”标签。还可以使用“Collectable”标签定义玩家可在场景中收集的物品。
标签对碰撞体控制脚本中的触发器很有用;例如,需要通过标签确定玩家是否与敌人、道具或可收集物进行交互。
更多内容请查看总目录【Unity】Unity学习笔记目录整理