Unity4.6新UI系統初探(uGUI)


一、引言

Unity終於在即將到來的4.6版本內集成了所見即所得的UI解決方案(視頻)。事實上從近幾個版本開始,Unity就在為這套系統做技術擴展,以保證最終能實現較理想的UI系統。本文試圖通過初步的介紹和試用,讓讀者對這套系統有大體的了解,以便更進一步評估這套UI系統好不好用,適合用在什么項目。為了避免坑挖太深,更進一步的試用和評估我將在《用uGUI開發自定義Toggle Slider控件》中進行論述。為論述方便,下文將這套New UI System簡稱為uGUI,並且以X-UI指代現有第三方UI插件。

(測試只針對Unity 4.6.0 beta 10,正式版可能會有所出入。目前Unity沒提供文檔,本人半桶水,歡迎群眾在微博或Issues里吐槽!)

二、Rect Transform

Rect Transform

Rect Transform繼承自Transform,是uGUI相比X-UI最顯著的區別[注1]。當你為Empty GameObject加入一個UI Component時,Transform會自動轉換為Rect Transform。Rect Transform盡量整合了X-UI常見的anchor(相對父物體的錨點), pivot(中點), stretch(拉伸)等屬性。值得一提的是,這里的anchor是Rect而非Vector2,因為它不僅用於偏移,而且用於縮放。點擊Rect Transform上的准心圖標,還能在彈出的Anchor Presets面板中對其進行快速設置。

Anchor Presets

這個面板還是不夠直觀,我們可以把它看成一張表,上面四個圖標用於設置列,左邊四個圖標用於設置行,也可以直接點擊里面的16個圖標同時設置行和列。強大的地方是,按住shift時能同時設置pivot,這時能發現控件雖然不動但position已經在改變。如果按住alt,則設置anchor的同時設置position。如果shift和alt同時按住,那么你就能同時設置anchor, pivot和position。這個操作方式比起X-UI,真的高明很多,對多分辨率適配很有幫助。

除此之外,Rect Transform還提供了Blueprint和Lock Rect選項,前者用於對旋轉過的元素進行定位,后者據說明是能在設置anchor時保持位置不變,暫時沒搞明白。

三、排序

SortHierarchy

uGUI可以直接在Hierarchy面板中上下拖拽來對渲染進行排序(支持程序控制),越上面的UI會越先被渲染,相比X-UI的global depth排序,這樣的拖拽設計很討好用戶。同時在結構上則和ex2D采用的local depth類似,這樣GO只和同級其它GO進行排序,開發組件會很方便。需要注意的是,這里排序只是相對UI而言,其它3D物體還是按原先的次序渲染,並且UI總是渲染在3D物體上面。這就導致你不能像用ex2D那樣直接將粒子系統插入到兩個UI之間。

這種無需填寫depth值的排序方式,容易導致沒有手工做sprite packing的free版用戶遇到draw call增加。因為所有物體的depth都是自動設置的,Unity保證了每個物體的depth都是唯一的。這時假設你有一個格子控件,每個控件用到了兩個Sprite,但你並沒有把Sprite都拼到同一張貼圖上。於是你每復制一個新的格子出來,draw call就會增加2個,因為Unity會以格子為單位依次繪制。pro用戶由於有sprite packing機制,不用擔心這個問題。(這種情況在ex2D里,是以默認提供"unordered"的渲染方式來解決的,這也是NGUI的默認做法。在這種情況下ex2D會優先以相同depth的相同Sprite為單位繪制,因此不論有多少個格子,draw call都是2個。除非你就是希望以格子為單位進行渲染[注6],那么你可以在ex2D里設置渲染方式為"ordered",或者在NGUI里給每個格子設置不同的depth。

四、控件

UI Component

uGUI自帶了以上控件,其中Image用於顯示Sprite,Raw Image用於顯示Texture,Image Mask和Rect Mask用於clipping。所有控件都是MonoBehaviour,可以直接從Inspector里拖到其它GameObject上。

4.1 Image

Image

uGUI用Image控件顯示圖片,圖片就是一個Sprite,這意味着Pro用戶不用再制作atlas了,相比X-UI是個大進步,Free用戶一樣可以手動做Packing。Image提供了Simple, Sliced, Tiled, Filled四種效果,和X-UI保持一致。

4.2 Button

Button

uGUI里,Button控件由兩個GameObject組成,一個包含Image, Button等Component,一個包含Text等Component。這樣設計很組件化,唯一的問題是當用戶想修改Button時,容易不小心選中Label或其它實體。

Button Component主要執行Transition和事件兩個操作。

  • Transition可選擇改變顏色、更換貼圖或自定義動畫,使用起來簡單方便,也能利用動畫定義更豐富的表現。我會再寫一篇文章演示Button的Transition。
  • 事件也是所見即所得的,在OnClick里面可以添加多個命令,命令可以選擇對應的目標、操作和參數。用法簡單,有需要也可以換程序控制。
    • 目標可以是任意Object,例如其它GameObject或者Project里的Asset
    • 操作可以是需要設置的參數或調用的方法
    • 參數分成Dynamic和Static,Dynamic能將控件的參數單向綁定到目標參數,Static則將目標參數設置成預設值。按鈕沒有Dynamic參數,Toggle, Slider等控件才有。

五、事件

5.1 Event Trigger

Event Trigger

uGUI控件往往只提供一個自帶事件,要響應更多基本事件的話,需要添加Event Trigger組件。Event Trigger包含以下事件:

  • PointerEnter, PointerExit, PointerDown, PointerUp, PointerClick
  • Move, Drag, Drop, Scroll
  • KeyDown, KeyUp, Select, Deselect

可以在Event Trigger中Add多個事件,每個事件都可以添加多個命令,用法和控件自帶事件一致。

5.2 Graphic Raycaster

Graphic Raycaster

每個Canvas都有一個Graphic Raycaster,用於獲取用戶選中的uGUI控件。多個Canvas之間通過設置Graphic Raycaster的priority來設置事件響應的先后次序。當Canvas采用World Space或Camera Space時,Graphic Raycaster的Block選項可以用來設置遮擋目標。

5.3 Event System

Event System

創建uGUI控件后,Unity會同時創建一個[注4]叫EventSystem的GameObject,用於控制各類事件。可以看到Unity自帶了兩個Input Module,一個用於響應標准輸入,一個用於響應觸摸操作。Input Module封裝了對Input模塊的調用,根據用戶操作觸發各Event Trigger。理論上我們可以編寫自己的Input Module,用來封裝各種外部設備的輸入,只要加入Event System所在的GameObject就行。

Event System組件則統一管理多個Input Module和各種Raycaster。它每一幀調用多個Input Module處理用戶操作,也負責調用多個Raycaster用於獲取用戶點擊的uGUI控件以及2D和3D物體。

六、性能

2D渲染分兩大類,一類是單純的Sprite繪制,用於渲染場景、角色、粒子等,另一類是UI繪制。Unity將這兩類需求划分成了SpriteRendereruGUI兩部分,前者由Transform + SpriteRenderer實現,后者由Rect Transform + CanvasRenderer + UI控件 + Canvas[注2]實現,這樣的兩套相對獨立的機制比起X-UI的UI控件繼承自SpriteRenderer更為合理。因為在2D游戲里SpriteRenderer只需要關心最基本的面片渲染,注重效率,而UI注重各類變換、對齊、操作、動畫,還常常需要Resize VBO。如果SpriteRenderer在設計上需要兼顧UI,就會像X-UI那樣設計得太過復雜,在用戶體驗和性能上都很不好。

這里我們探討一下uGUI的渲染機制,當我們渲染多個使用相同Sprite的控件時,並沒發生dynamic batching,但是drawcall也沒有上升。這就說明Unity在內部使用了專門的一套batching機制,把多個控件的VBO事先合並成了一個。也就是說CanvasRenderer不負責實際渲染,而是由Canvas批量渲染多個CanvasRenderer,這和部分X-UI采用的做法一致。這樣單獨batch的設計有可能使得性能比SpriteRenderer好,也可能導致性能更差。性能會更好的情況在ex2D里已經證實了,主要原因是這樣能更好的平衡CPU和GPU負載,並且能做到更優化的batching算法。性能更差的情況,在去年舊版的NGUI測試時也遇到了,根本原因還是優化不到位導致的(不是貶低,不同工具的取舍和面向市場都不同)。而Unity的 SpriteRenderer在手機上的渲染跑分是和ex2D持平的,CanvasRenderer又比SpriteRenderer快[注3],因此uGUI的性能不用擔心。由於目前沒有Mac版本,我會在正式版發布后進行一次手機跑分測試。

七、小結

uGUI功能完善,操作簡潔,很接地氣。可以說uGUI是相對X-UI的全面升級,整體架構更為嚴謹,實現更為清晰。依托4.5的Module Manager,uGUI以Package的形式提供,也能獲得快速的升級[注5]。作為ex2D v2.0開發者之一,我很看好它將來的發展,uGUI將在大多數場合取代X-UI。

初步感受:

7.1 亮點

  • RectTransform
  • Event/單向數據綁定
  • 直接在Hierarchy中排序
  • Pro用戶可用Sprite的動態拼圖,無需手工拼圖

7.2 不足

7.3 小遺憾

  • Anchor Presets面板還不夠直觀。
  • 用戶想修改Button時,很容易修改到Label。
  • 當Hierarchy面板內的目標節點展開子節點后,無法將其它節點直接拖動到目標的正下方。

7.4 小問題

  • Input組件對方向鍵的支持有問題。
  • Game View dock到主窗口后,top定位有誤,把toolbar的高度也算進去了。

八、附注

  1. 我們在其它平台上開發類 Entity-Component框架時,討論過Unity為什么不在底層對transform做特殊處理,以避免插件作者手工緩存transform來優化query transform引起的開銷,甚至是將transform直接整合進GameObject。原因是現在的transform是3D的,將來完全可能推出 2D Transform。所以Unity在之前的版本里一直保留着transform的獨立性。
  2. 我不能完全肯定一定是Canvas,但通過Canvas和CanvasRenderer的接口來看,這個可能性很大。
  3. 基於更好的平衡CPU和GPU負載 + 更優化的batching算法,以Unity的實力CanvasRenderer超越SpriteRenderer問題不大。而且如果性能不會提升,uGUI只要像2D Toolkit那樣給每個控件直接添加MeshRenderer,也就是說uGUI直接用已有的SpriteRenderer就好,不太可能加入新的CanvasRenderer性能反而更慢。
  4. Unity允許多個Event System同時存在,但同一時刻只有一個能夠生效。
  5. uGUI的控件、Event等模塊以包的形式提供,位於程序目錄下的%UNITY%\Editor\Data\UnityExtensions\Unity\GUISystem\4.6.0,Unity 提供了兩個運行時版本的DLL,分別用於創作和發布。區別主要是發布版不含一些Editor中才用得到的代碼。由於DLL沒辦法通過預編譯符號來進行條件編譯,因此Unity使用這種方式進行權衡,用戶發布時無需手工切換DLL版本,滿足了閉源,又兼顧了執行效率。這樣就甩開了第三方插件幾條街,很多插件在這個問題上不是犧牲性能就是無奈開源。
  6. 有時還是會需要以格子為單位渲染,例如當格子之間需要重合,這種需求在UI里不常見。

文獻資料

本文作者:Jare @ 夢加網絡

本文轉載自: https://github.com/jaredoc/unity-ugui/tree/master/overview


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM