Unity層級淺談

封面來源于網(wǎng)絡(luò)

前言

用于管理本知識集所有源代碼的github庫此前一直以分支的形式對應(yīng)不同的知識點。現(xiàn)在開來這樣做是有點麻煩的。各位拉取之后還得遷到對應(yīng)分支。我現(xiàn)在改為全部在master下新建項目的形式,大家拉取之后打開對應(yīng)的項目就行了。原本的master分支我已經(jīng)遷出一個OldMaster保留下來,似乎對應(yīng)的是我的這篇文章:UGUI動態(tài)加載對話框.

好了,言歸正傳,我們來談?wù)劷裉斓脑掝},Unity中的層級(layer).

什么是Layer

層級是Unity中場景物體的一種屬性。攝像機可以指明渲染層級以渲染場景中的部分物體。燈光可以指明照射層級以照亮部分物體(可以指定照亮某些層級的物體以顯示陰影)。層級還能用于設(shè)置物理碰撞關(guān)系。

添加與設(shè)置Layer

添加與設(shè)置Layer都可以選中一個物體之后在Inspector面板中操作。如下圖所示:

查看MainCamera的層級
查看已有層級

Unity內(nèi)置了5個層級,并預(yù)留了3個層級給引擎使用。用戶可以自定義序號為8至31的層級。總共有32個層級是方便用戶使用int類型做位操作取得想要的層級組合。

用法實驗

攝像機與燈光

我們來創(chuàng)建一個簡單的場景,以更好的領(lǐng)會層級的用法。

新建一個Unity工程,默認場景中已經(jīng)有一個MainCamera和一個Directional Light。新建一個Plane作為我們的地面。新建一個Cube物體為其添加Cube層級。新建一個Sphere物體為其添加Sphere層級。新建一個Capsule物體保留Default層級。保存場景為UseLayer。

指定MainCamera的Culling Mask為不拍攝Sphere,如圖所示:

攝像機的拍攝層級

指定Directional Light的照射層級為不包含Cube,如圖所示:

Directional Light的照射層級

調(diào)整物體與攝像機的位置及角度,使攝像機的拍攝視錐能夠包含場景中的所有物體,最終可以達到如下圖的效果:

運行效果

可以看到我們拍攝了Game視圖中沒有渲染Sphere物體和其影子,渲染了Cube物體卻沒有影子。Scene視圖的攝像機是渲染所有層級物體的,可以用于比較。在Scene視圖中Sphere是有影子的,說明我們的Directional Light是照射了Sphere層級的物體的。Capsule物體只用于比較。

物理檢測

層級另外的用法是物理檢測。點擊菜單Edit->Project Settings->Physics.可以查看項目的物理碰撞設(shè)置:

物理層級碰撞矩陣

這里的設(shè)置將影響OnCollisionEnter等消息的發(fā)送。

而射線檢測本就要去指定檢測層級:

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

關(guān)于子物體層級的設(shè)置與討論

在游戲中,我們經(jīng)常需要動態(tài)指定物體的層級,而Unity目前沒有提供指定物體子節(jié)點的層級的接口。如果只是簡單的設(shè)置物體的layer屬性,是無法改變物體子節(jié)點的層級的。我們需要自己實現(xiàn)改變子物體層級的接口。

Unity的官方社區(qū)和論壇都有人討論這個問題。這里附上鏈接供大家參考:

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

我參照網(wǎng)上的做法也寫了一個測試。在剛剛的工程中新建一個場景,命名為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);
        }
    }
}

測試結(jié)果是三種寫法消耗時間差不多,普遍GetComponentsInChildren更快。

結(jié)束語

如果你喜歡本文,那就點個喜歡吧。本文的github庫在這里,歡迎大家fork。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容