Unity层级浅谈

发表于2016-10-16
评论0 3.4k浏览
前言
  用于管理本知识集所有源代码的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等消息的发送。
而射线检测本就要去指定检测层级:

1
2
3
4
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下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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。
  文/kashiwa(简书作者)
  原文链接:http://www.jianshu.com/p/8184af0f1a12
  著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
腾讯GAD游戏程序交流群:484290331Gad游戏开发核心用户群

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论