Unity层级浅谈

封面来源于网络

前言

用于管理本知识集所有源代码的github库此前一直以分支的形式对应不同的知识点。现在开来这样做是有点麻烦的。各位拉取之后还得迁到对应分支。我现在改为全部在master下新建项目的形式,大家拉取之后打开对应的项目就行了。原本的master分支我已经迁出一个OldMaster保留下来,似乎对应的是我的这篇文章:UGUI动态加载对话框.

好了,言归正传,我们来谈谈今天的话题,Unity中的层级(layer).

什么是Layer

层级是Unity中场景物体的一种属性。摄像机可以指明渲染层级以渲染场景中的部分物体。灯光可以指明照射层级以照亮部分物体(可以指定照亮某些层级的物体以显示阴影)。层级还能用于设置物理碰撞关系。

添加与设置Layer

添加与设置Layer都可以选中一个物体之后在Inspector面板中操作。如下图所示:

查看MainCamera的层级
查看已有层级

Unity内置了5个层级,并预留了3个层级给引擎使用。用户可以自定义序号为8至31的层级。总共有32个层级是方便用户使用int类型做位操作取得想要的层级组合。

用法实验

摄像机与灯光

我们来创建一个简单的场景,以更好的领会层级的用法。

新建一个Unity工程,默认场景中已经有一个MainCamera和一个Directional Light。新建一个Plane作为我们的地面。新建一个Cube物体为其添加Cube层级。新建一个Sphere物体为其添加Sphere层级。新建一个Capsule物体保留Default层级。保存场景为UseLayer。

指定MainCamera的Culling Mask为不拍摄Sphere,如图所示:

摄像机的拍摄层级

指定Directional Light的照射层级为不包含Cube,如图所示:

Directional Light的照射层级

调整物体与摄像机的位置及角度,使摄像机的拍摄视锥能够包含场景中的所有物体,最终可以达到如下图的效果:

运行效果

可以看到我们拍摄了Game视图中没有渲染Sphere物体和其影子,渲染了Cube物体却没有影子。Scene视图的摄像机是渲染所有层级物体的,可以用于比较。在Scene视图中Sphere是有影子的,说明我们的Directional Light是照射了Sphere层级的物体的。Capsule物体只用于比较。

物理检测

层级另外的用法是物理检测。点击菜单Edit->Project Settings->Physics.可以查看项目的物理碰撞设置:

物理层级碰撞矩阵

这里的设置将影响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<Transform>(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<Transform> moveTargets = new Stack<Transform>();
        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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容