Unity层级浅谈

Unity层级浅谈
文章图片
封面来源于网络 前言 用于管理本知识集所有源代码的github库此前一直以分支的形式对应不同的知识点。现在开来这样做是有点麻烦的。各位拉取之后还得迁到对应分支。我现在改为全部在master下新建项目的形式,大家拉取之后打开对应的项目就行了。原本的master分支我已经迁出一个OldMaster保留下来,似乎对应的是我的这篇文章:UGUI动态加载对话框.
好了,言归正传,我们来谈谈今天的话题,Unity中的层级(layer).
什么是Layer 层级是Unity中场景物体的一种属性。摄像机可以指明渲染层级以渲染场景中的部分物体。灯光可以指明照射层级以照亮部分物体(可以指定照亮某些层级的物体以显示阴影)。层级还能用于设置物理碰撞关系。
添加与设置Layer 添加与设置Layer都可以选中一个物体之后在Inspector面板中操作。如下图所示:
Unity层级浅谈
文章图片
查看MainCamera的层级 Unity层级浅谈
文章图片
查看已有层级 Unity内置了5个层级,并预留了3个层级给引擎使用。用户可以自定义序号为8至31的层级。总共有32个层级是方便用户使用int类型做位操作取得想要的层级组合。
用法实验 摄像机与灯光 我们来创建一个简单的场景,以更好的领会层级的用法。
新建一个Unity工程,默认场景中已经有一个MainCamera和一个Directional Light。新建一个Plane作为我们的地面。新建一个Cube物体为其添加Cube层级。新建一个Sphere物体为其添加Sphere层级。新建一个Capsule物体保留Default层级。保存场景为UseLayer。
指定MainCamera的Culling Mask为不拍摄Sphere,如图所示:
Unity层级浅谈
文章图片
摄像机的拍摄层级 指定Directional Light的照射层级为不包含Cube,如图所示:
Unity层级浅谈
文章图片
Directional Light的照射层级 【Unity层级浅谈】调整物体与摄像机的位置及角度,使摄像机的拍摄视锥能够包含场景中的所有物体,最终可以达到如下图的效果:
Unity层级浅谈
文章图片
运行效果 可以看到我们拍摄了Game视图中没有渲染Sphere物体和其影子,渲染了Cube物体却没有影子。Scene视图的摄像机是渲染所有层级物体的,可以用于比较。在Scene视图中Sphere是有影子的,说明我们的Directional Light是照射了Sphere层级的物体的。Capsule物体只用于比较。
物理检测 层级另外的用法是物理检测。点击菜单Edit->Project Settings->Physics.可以查看项目的物理碰撞设置:
Unity层级浅谈
文章图片
物理层级碰撞矩阵 这里的设置将影响OnCollisionEnter等消息的发送。
而射线检测本就要去指定检测层级:

int layerMask = LayerMask.NameToLayer("Cube"); if (Physics.Raycast(transform.position, Vector3.forward, Mathf.Infinity, layerMask)) Debug.Log("The ray hit the Cube");

关于子物体层级的设置与讨论 在游戏中,我们经常需要动态指定物体的层级,而Unity目前没有提供指定物体子节点的层级的接口。如果只是简单的设置物体的layer属性,是无法改变物体子节点的层级的。我们需要自己实现改变子物体层级的接口。
Unity的官方社区和论坛都有人讨论这个问题。这里附上链接供大家参考:
http://forum.unity3d.com/threads/change-gameobject-layer-at-run-time-wont-apply-to-child.10091/
http://answers.unity3d.com/questions/168084/change-layer-of-child.html
http://answers.unity3d.com/questions/26479/fast-layer-assignment.html
我参照网上的做法也写了一个测试。在刚刚的工程中新建一个场景,命名为SetLayer。新建脚本如下,并挂载MainCamera下:
using System.Collections.Generic; using UnityEngine; public class SetLayer : MonoBehaviour {void Start() { GameObject root = Util.CreateHeirarchy(); float startTime = Time.realtimeSinceStartup; Util.SetLayerOnAll(root, LayerMask.NameToLayer("Cube")); float totalTimeMs = (Time.realtimeSinceStartup - startTime) * 1000; print("Set layer on all time: " + totalTimeMs + "ms"); startTime = Time.realtimeSinceStartup; Util.SetLayerRecusively(root, LayerMask.NameToLayer("Sphere")); totalTimeMs = (Time.realtimeSinceStartup - startTime) * 1000; print("Set layer on all recursive time: " + totalTimeMs + "ms"); startTime = Time.realtimeSinceStartup; Util.SetLayerNotRecusively(root.transform, LayerMask.NameToLayer("Cube")); totalTimeMs = (Time.realtimeSinceStartup - startTime) * 1000; print("Set layer on not recursive time: " + totalTimeMs + "ms"); }}public static class Util { public static GameObject CreateHeirarchy() { GameObject root = new GameObject(); GameObject[] children = new GameObject[100]; for (int i = 0; i < 100; i++) { GameObject child = new GameObject(); child.transform.parent = root.transform; children[i] = child; }GameObject[] grandchildren = new GameObject[1000]; for (int i = 0; i < 1000; i++) { GameObject grandchild = new GameObject(); grandchild.transform.parent = children[Random.Range(0, 99)].transform; grandchildren[i] = grandchild; }for (int i = 0; i < 10000; i++) { GameObject greatgrandchild = new GameObject(); greatgrandchild.transform.parent = grandchildren[Random.Range(0, 999)].transform; }return root; }public static void SetLayerOnAll(GameObject obj, int layer) { if (null == obj) return; foreach (Transform trans in obj.GetComponentsInChildren(true)) { trans.gameObject.layer = layer; }}public static void SetLayerRecusively(GameObject obj, int layer) { if (null == obj) return; obj.layer = layer; foreach (Transform child in obj.transform) SetLayerRecusively(child.gameObject, layer); }public static void SetLayerNotRecusively(Transform root, int layer) { Stack moveTargets = new Stack(); moveTargets.Push(root); Transform currentTarget; while (moveTargets.Count != 0) { currentTarget = moveTargets.Pop(); currentTarget.gameObject.layer = layer; foreach (Transform child in currentTarget) moveTargets.Push(child); } } }

测试结果是三种写法消耗时间差不多,普遍GetComponentsInChildren更快。
结束语 如果你喜欢本文,那就点个喜欢吧。本文的github库在这里,欢迎大家fork。

    推荐阅读