Unity NGUI和UGUI與模型、特效的層級關系

目錄

1、介紹兩大UI插件NGUI和UGUI
2、unity渲染順序控制方式
3、NGUI的控制
4、UGUI的控制
5、模型深度的控制
6、粒子特效深度控制
7、NGUI與模型和粒子特效穿插層級管理
8、UGUI與模型和粒子特效穿插層級管理

寫在前面

這篇筆記是整理了之前做的記錄,在做項目的過程中,遇到了各種各樣的界面穿插問題,界面層級混亂,比如,手機卡了或點快了,就導致兩個界面相互交叉。對于界面,這應該算是一個很嚴重的bug,很大部分原因是整個UI框架沒有從整體上考慮這個,后來決心弄清楚層級的控制,并把一些對于目前項目可行的方法應用,界面穿插的問題少了很多,注意我只是在現(xiàn)有的框架打的補丁。如果是一個從頭開始,在架構UI的時候,這篇筆記應該會很有用。

以前項目使用的NGUI插件,UI是有一個人擺放的,我們客戶端就拿到這些prefab,添加相應的邏輯腳本。但恰恰是這個擺界面的人也沒有注意界面的層級,各個panel、各個weight的depth沒有統(tǒng)一管理,導致開發(fā)中后期,界面一多起來,很多界面同時出現(xiàn)的時候,panel和weight的層級就亂了。

unity有哪些GUI框架
  1. unity自帶的GUI系統(tǒng)
    一般都不用它來開發(fā)游戲,作為拓展unity編輯器開發(fā)比較多,unity自帶的GUI效率非常低,每次渲染都是一個DrawCall,不好用,不能做到所見即所得。

  2. unity4.6之后的UGUI
    UGUI是NGUI作者參與開發(fā)的,unity官方的新UI系統(tǒng),感覺挺好用的,至于效率問題應該比NGUI好些。5.x以后ugui有很大的提升,以后ugui應該會逐步替代ngui。
    很早之前就嘗試過這套ugui系統(tǒng),正是我在研究界面層級的時候,用NGUI顯示模型粒子特效沒有找到好的方法時,于是探索了ugui,具體怎么控制層級,后面會討論。

  3. NGUI
    是一個老牌的unity UI插件了,在ugui沒有推出之前,大部分unity的游戲都使用的NGUI插件開發(fā)UI,它的好處很多,提供了常見的UI控件,實現(xiàn)幾乎所有需要的功能,在效率上也是控制嚴謹,DrawCall合并極大提升了控件渲染效率,還支持3D GUI。但是也有很多不足,版本更新太頻繁,版本bug很多,對于了解NGUI的開發(fā)者可以很好利用它,但對于初學者來說,NGUI固然容易上手好用,但是對DrawCall的重建規(guī)則不了解,仍然會效率低下(后面會整理一下分析NGUI的筆記)。

  4. FairyGUI
    最近發(fā)現(xiàn)的一款跨平臺UI編輯器,組合各種復雜UI組件,以及為UI設計動畫效果,無需編寫代碼,可以一鍵導出Unity,Starling,Egret, LayaAir,F(xiàn)lash等多個主流應用和游戲平臺。

特性:
所見即所得。操作簡易,使用習慣與Adobe系列軟件保持一致,美術設計師可以輕松上手。
在編輯器即可組合各種復雜UI組件,無需編寫代碼。不需要程序員編碼擴展UI組件。
強大的文本控件。支持動態(tài)字體,位圖字體,以及BMFont制作的位圖字體,支持HTML語法和UBB語法,支持圖文混排。
強大的列表控件,支持多種布局,支持虛擬列表和循環(huán)列表,即使列表項目數(shù)量巨大也拒絕卡頓。
支持圖片的九宮格和平鋪處理,支持圖片變色和灰度,支持序列幀動畫編輯和使用。
內(nèi)置手勢支持。
提供時間軸設計UI動效,可實時看到每幀的位置或其他效果。
編輯狀態(tài)下使用分散的素材,發(fā)布時自動打包圖集。支持定義多個圖集,自動支持抽出A通道的壓縮方式。
多國語言支持。
各種分辨率自適應。
提供插件機制,可以根據(jù)項目的需要為編輯器加入個性功能。
為各個游戲平臺提供了一致的API,得益于編輯器強大的編輯功能,程序員只需要了解少量API就能完成UI展現(xiàn),相比Feathers, NGUI, UGUI等UI框架,F(xiàn)airyGUI提高了UI制作效率,降低了成本。

當然不止這些,還有許多UI框架,比如:Daikon Forge GUI、EZ GUI、2dTookit等等,有時間可以看看,但目前主要用到的還是NGUI、UGUI啦。

為什么要關心渲染順序?
  1. 如果是2D游戲,渲染順序關系著每個層次的顯示先后,比如UI在游戲內(nèi)容前面,游戲內(nèi)容又有多層次。舉一個簡單的例子,在橫版2D游戲中,經(jīng)常會用到多層滾動的背景,把游戲物體分層管理起來,可以有效的減少出錯幾率,很好的控制顯示效果。

  2. 對于3D游戲,游戲內(nèi)容是3D的,UI一般是2D,有很多時候需要把某個模型啊,粒子特效啊,放在界面上,這樣就有一個問題,3D物體和2D界面的先后關系,比如有些界面是在模型之上的,有些在下面,嘗試過很多種辦法,都能實現(xiàn)需求,但不是每種辦法都是那么舒服的。

unity中控制渲染順序的方式

  1. Camera
    Camera是unity中最優(yōu)先的渲染順序控制。depth越大,渲染順序越靠后。


    clipboard.png
  2. sortingLayer 和 sortingOrder
    對于這個屬性,我也是云里霧里的,不是太明白。按照字面意思是層的排序,Canvas和Renderer都有這個屬性,unity官方就一句話帶過。優(yōu)先級僅次于Camera的depth。


    clipboard.png

看網(wǎng)上的博客,一般都寫著sorting layer是比Camera低一層級的控制渲染順序的屬性。然后我做了很多嘗試,發(fā)現(xiàn)并不是所有Renderer組件這個屬性作用,根據(jù)嘗試的結果,做如下總結:

Canvas中的sorting layer可以控制Canvas的層級,這是ugui中的東西,下面會討論的;

GameObject中的renderer屬性就是掛在該GameObject的Renderer組件,Renderer是渲染組件的基類,下面有多個派生類,經(jīng)過測試,目前知道的只有SpriteRenderer的sorting layer和sorting order能控制渲染順序。所以我也覺得,sorting layer和 sorting order就是unity 對2D游戲所支持的吧;

renderer組件有如下幾類:


clipboard.png
  1. RenderQueue
    這是unity中的一個概念,(至少之前在opengl中沒聽過),大致意思就是渲染順序,那無疑就是控制渲染順序的嘛。


    clipboard.png

    所以一般設置材質(zhì)的renderQueue或直接在shader中設置。

在ShaderLab中,有4個提前定義好的render queue,你可以設置更多的在他們之間的值:

Background :表示渲染在任何物體之前
Geometry(default):渲染大多數(shù)幾何物體所用的render queue
AlphaTest:用于alpha測試
Transparent:用于渲染半透明物體
Overlay:渲染所有物體之上

There are four pre-defined render queues, but there can be more queues in between the predefined ones. The predefined queues are:
Background - this render queue is rendered before any others. You’d typically use this for things that really need to be in the background.
Geometry (default) - this is used for most objects. Opaque geometry uses this queue.
AlphaTest - alpha tested geometry uses this queue. It’s a separate queue from Geometry one since it’s more efficient to render alpha-tested objects after all solid ones are drawn.
Transparent - this render queue is rendered after Geometry and AlphaTest, in back-to-front order. Anything alpha-blended (i.e. shaders that don’t write to depth buffer) should go here (glass, particle effects).
Overlay - this render queue is meant for overlay effects. Anything rendered last should go here (e.g. lens flares).

  1. 空間深度
    在攝像機坐標系下的Z軸,控制著該相機下的物體的深度,在fragment shader中進行深度測試,這樣就控制了渲染到屏幕的順序。(可以看看深度測試的具體原理,就明白怎么控制顯示的先后順序了)

NGUI控制渲染順序的方式

好了,講完了unity中的控制渲染順序的方法后,接下來聊兩個個UI系統(tǒng)獨有的方法了。首先說明一下,NGUI中空間位置不會影響屏幕顯示關系,因為在它內(nèi)部處理頂點的時候,舍棄了Z坐標值。

  1. UIPanel
    首先講到的就是UIPanel,用過NGUI插件的朋友應該非常熟悉這個組件,將來會整理一篇分析NGUI的筆記,里面會重點聊到UIPanel,所以這篇筆記就一筆帶過了:UIPanel非常重要。哈哈。

  2. depth:
    NGUI中最正統(tǒng)的控制panel之間層級關系的就是 它的 depth 屬性。depth越大,越靠后渲染。


    clipboard.png
  3. sorting order:
    panel的該屬性也可以控制panel的順序。


    clipboard.png
拓展

它的優(yōu)先級在depth之前,內(nèi)部還是通過設置render的sorting order來控制的。來看看一些源碼:

UIPanel.cs

 public int sortingOrder
 {
  get
  {
   return mSortingOrder;
  }
  set
  {
   if (mSortingOrder != value)
   {
    mSortingOrder = value;
#if UNITY_EDITOR
    NGUITools.SetDirty(this);
#endif
    UpdateDrawCalls();
   }
  }
 }

然后查找了mSortingOrder的索引,發(fā)現(xiàn)在函數(shù)UpdateDrawCalls中使用了mSortingOrder


clipboard.png

繼續(xù)跟蹤dc.sortingOrder


clipboard.png

然后發(fā)現(xiàn)mRenderer是個 MeshRenderer類型的,在之前還說過,對render的sorting order不太明白,并不能控制渲染順序。然而NGUI內(nèi)部卻使用了并且能夠控制渲染順序。我就納悶了,然后在網(wǎng)上查了很久的資料,終于在雨松博客的評論區(qū)看到:unity中3D物體的Z的渲染區(qū)域 和UI的區(qū)域不一樣,必須同樣都是一個片才行。NGUI中生成的UIDrawCall里面的Mesh都是片,所以sorting order對它有用,而之前說的之間創(chuàng)建一個cube,它是一個3D物體,3D物體和UI沒辦法通過它來控制層級的。

為什么panel中的depth比sorting order優(yōu)先級低呢?

是因為panel的depth控制著UIDrawCall的生成順序,影響了RenderQueue的順序,前面已經(jīng)提到過了,sorting order和sorting layer比RenderQueue優(yōu)先級更高。

  1. UIWeight的depth
    在同一個panel下的各個weight,可以用weight的depth屬性來控制它們的前后關系。depth控制著weight在panel頂點重建時傳入的頂點序列,這個跟蹤一下NGUI源碼就知道了。(后面我會整理幾篇分析NGUI的文章,里面包含這個)

  2. RenderQueue
    在NGUI中使用這個屬性,就得先看看UIPanel關于RenderQueue的三種方式:


    clipboard.png

Automatic:由NGUI自動生成
StartAt:手動設置一個值
Explicit:同一個panel下的RenderQueue的值相同

看代碼可以理解一下,UIPanel.cs


clipboard.png

UGUI控制渲染順序的方式

  1. Canvas
    不同Canvas之間可以用以下兩個屬性控制渲染層級
    Sorting Layer
    Order in Layer

Canvas和NGUI的UIPanel一樣,這些計算都不會管兩個之間的父子關系,有一個算一個。

  1. Hierarchy中順序
    在同一個Canvas中,Hierarchy的順序決定了控件的層級關系。

模型深度的控制

  1. 空間深度
    對于3D物體的顯示先后就是完全按空間的先后來的,當然可以在fragment shader中關閉深度測試,或進行其他影響幀緩沖區(qū)的操作。就跟opengl中一樣了。

  2. RenderQueue
    RenderQueue是對unity中所有可以渲染的物體都適用。

補充:用Sorting Order可以控制片模型的層級關系,NGUI中sorting order就是就是靠這種特性實現(xiàn)的。

粒子特效渲染層級的控制

  1. 空間深度
    和3D模型一致。

  2. RenderQueue
    和3D模型一致。

  3. sorting order
    粒子系統(tǒng)本身是一個Renderer組件,它渲染的是一個一個精靈,是一個一個片,該屬性有效。

NGUI中讓模型穿插在兩個界面之間

經(jīng)過上面的整理,已經(jīng)明白了各個地方是可以怎么控制渲染順序了,接下來就來解決一些項目中遇到的問題。

NGUI中放3D模型,實現(xiàn)方法:

  1. 多個相機
    這種方法肯定可以實現(xiàn),而且簡單粗暴。但仔細想想,要做的決不僅僅加個相機這么簡單,我之前做的項目就是這么搞得。然后……然后……在開發(fā)的中后期,各種bug就出來了,由于多相機沒有管理好,各種問題真的很難纏,呵呵底層不是我寫的哈,所以我們上層開發(fā)人員就補丁啊,整個項目代碼有些地方面目全非。

多相機其實就是利用Camera的depth來控制渲染順序的,一般來講,模型一個相機,UI一個相機,等等……你以為兩個相機就夠了嗎?有沒有想過模型上可能還有界面呢?你可能說再加一個相機,但是有些需求每個界面的跳轉(zhuǎn)都是多個的,并不能直接在做界面的時候就確定哪個界面在上面哪個界面在下面,所以這樣的加多個相機并不可行。

那應該怎么做?

后來想到一種辦法,就是每個界面對應一個相機,一個相機照一個模型,或是在一起顯示的模型,利用相機的depth來控制它們的遮擋關系,但是需要在客戶端框架中管理好相機,做一個“池子”,讓相機可以復用,并且。可能你會擔心性能問題,但這不是問題,一個游戲中可能有很多界面,但是需要同時顯示的最多不超過5、6個吧,加上模型需要的,每次同時工作的撐死了就10個了,而且只要讓相機只照射到相應的界面,這就不會造成性能的損失,不同的只是變換矩陣而已。

  1. 用RenderQueue控制
    相比于相機的管理,我覺得用RenderQueue來控制會更簡單一些,畢竟NGUI中也大量使用了RenderQueue來控制前后關系,可以看看它們源碼:


    截圖.png

這是在UIPanel中的LateUpdate,可以看到三種模式下,RenderQueue具體值加的方式,一般我們都是用Automatic模式,這種模式下是根據(jù)每個UIPanel中生成的DrawCall自動計算RenderQueue的值。

不管3D模型、粒子特效、還是NGUI,都可以用RenderQueue來控制渲染順序,所以我想到了一個辦法。

修改NGUI的源碼,讓兩個UIPanel的RenderQueue值間隔一些,空出幾個值用來設置給模型或粒子特效,很簡單:


截圖.png

看吧,是不是很簡單。然后只需要設置顯示在界面上的模型或粒子特效的RenderQueue為這些空出來的值就行了。


截圖.png

這個腳本掛在相應的模型上,target表示 該模型要顯示在哪個panel下面。

A、B兩個界面,我把渲染順序調(diào)整后:A --- 模型 --- B

到這里只做好了一部分,我們只是利用RenderQueue控制了渲染順序,但是空間的深度關系還是在影響著在屏幕上顯示的先后關系。在fragment shader中會進行深度測試,不管你誰先渲染誰后渲染,只要所有物體渲染時都寫入深度緩沖區(qū),那么渲染順序并不能真正影響最后屏幕顯示的先后關系。

所以還需要一步,就是在渲染模型時關閉深度測試,例如A、B兩個界面,模型在AB之間,那么渲染流程如下:

A --- 關閉深度測試 --- 模型 --- 開啟深度測試 --- B

在shader lab中關閉深度測試的方法是:

 ZWrite Off

這里提供一個簡單的shader:

Shader "Custom/DepthTest" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader {
        Tags { "Queue"="Transparent" }
         
        ZWrite Off
         
        LOD 200
         
        CGPROGRAM
        #pragma surface surf Lambert
 
        sampler2D _MainTex;
 
        struct Input {
            float2 uv_MainTex;
        };
 
        void surf (Input IN, inout SurfaceOutput o) {
            half4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

補充:模型與模型之間也可以用上面的這種渲染流程控制屏幕顯示的先后關系。

  1. 每次只顯示一個界面
    在同一時間,屏幕上只顯示一個界面,每次打開新界面就隱藏下面的界面,連模型和粒子都隱藏掉。這樣就不存在兩個界面同時顯示的情況了。

  2. 空間深度
    在其它控制條件都相同的情況下,NGUI和模型在一個相機下,受正常的渲染管線控制,也就是空間位置關系可以影響渲染到屏幕的層級關系。注意模型的“厚度”。

NGUI中讓粒子特效穿插在兩個界面之間

粒子特效也可以使用Camera的depth來控制,但是粒子特效會存在很多,所以用Camera并不可行,所以這里就直接排除掉了。

  1. Sorting Order
    粒子特效本身是用“點精靈”渲染的,每個粒子就是一個點精靈,可以看做一個片模型,而片模型就可以受該屬性影響。

可以做一個測試,把下面腳本掛在粒子特效上:

public class SortUtil : MonoBehaviour {
    void Awake()
    {
        renderer.sortingOrder = 1;
    }
}

sorting Order默認值為0,現(xiàn)有A、B兩個界面,把A的panel設置為0, B的panel設置2:

A:0
粒子特效:1
B:2

粒子特效剛好插在A、B之間,顯示效果也是粒子特效穿插在A、B之間。

補充:unity5.3后粒子特效支持了直接設置sorting order


截圖.png
  1. RenderQueue
    這里就沒有模型和NGUI交叉麻煩了,只需要完成RenderQueue的設置就可以了,不需要改shader。

假設A、B兩個界面,RenderQueue的值的大小關系:
A < 粒子特效 < B

參照上面模型的具體方法,讓粒子特效的RenderQueue插在A、B兩個界面之間就可以了。

UGUI中讓模型穿插在兩個界面之間

  1. 多個相機
    同樣的,可以用多個相機來做,但這種方式總歸是不太好,不推薦。

  2. shader lab
    在fragment shader中利用傳入的Mask數(shù)據(jù),去模型片段數(shù)據(jù)進行篩選,符合條件的留下,不符合的丟棄。這種辦法其實也適用于NGUI。

具體怎么做的可以參考雨松momo的一篇博文:這里

  1. RenderTexture
    UGUI中Image可以接受一個材質(zhì),可以把RenderTexture放在一個材質(zhì)上。這樣就可以按照UGUI本身的那些排序方式來控制了。

UGUI中讓粒子特效穿插在兩個界面之間

  1. sorting order
    和NGUI一樣,同樣可以用sorting order來控制,Canvas組件支持這些屬性。


    截圖.png

雨松momo也過一篇文章,我這里就不再說了,這里

總結

寫了這么多,大致總結了常見的方式,比較熟悉NGUI,所以對于NGUI中各種都比較清楚,寫得比較詳細。對UGUI可能總結的不是很完整,所以以后還會繼續(xù)總結。在文章中提到了幾處shader lab的地方,我在這里說明一下,利用shader lab也可以完成以上各種遮擋關系,層級關系,但這篇文章沒有詳細講清楚,主要考慮到篇幅,所以后來會專門寫shader lab的實現(xiàn)方法,其思想和雨松momo的差不多。

寫在最后

以上這些東西都是在項目中實際遇到的問題,以及思考并試著用過的,現(xiàn)在將筆記整理成這篇文章,有什么錯誤直接留言一起討論,一起成長。

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

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