UGUI的一些效能優化經驗

2021-08-25 13:55:57 字數 3427 閱讀 2330

自從unity問世以來,ui一直都存在比較大的問題,自帶的ongui不能所見即所得,製作過程比較麻煩。於是出現了很多第三方的優秀的ui外掛程式,比如很多專案裡面用到的ngui,或者後來出的fairygui。unity官方在4.x時代開始推出了自己的新ui系統,名為ugui。由於是官方出品,所以選擇使用的人也比較多。

阿趙我也是基於信任官方出品的心理,在最近的兩個專案裡面都使用ugui進行開發。ugui是乙個比較完整的所見即所得式的ui系統, 不過說實在的,我個人感覺它的成熟度還是差了點,有很多需要注意的地方,不然就會產生很多效能上的問題,比如drawcall問題和執行時的效率問題。我就曾經在專案中遇到了,ugui用的不好,它產生的消耗比3d場景內的地圖和角色消耗還高的情況。老實說,我用ugui也用的不好,這裡也只是分享一點經驗,這次主要講一下ui重繪的消耗和圖集的一些問題。

要了解ugui的一些坑,首先要明白它是基於什麼原理來運作的。

首先,ugui還是基於網格模型渲染的。乙個矩形,實際上還是由4個頂點組成的乙個mesh顯示出來的,和正常的mesh模型渲染一樣,它使用了材質球和貼圖。我們平常看不到這些元素,是因為渲染ui用的mesh網格是在執行中才生成的,它預設使用了untiy內建的乙個預設的材質球,然後使用我們指定的ui作為貼圖。

開啟乙個image來看看,可以看出material是空的,那是因為它有預設的材質球。然後unity也允許你再指定乙個material來渲染。值得注意的是,這個材質球如果你是直接掛在ui的image上面,那麼如果你在執行時修改material,將會改到的是share的原始material。所以最好的做法是在**裡面生成乙個材質球的複製例項,然後再賦給image使用。

明白了單個ui元件的渲染原理之後,接下來需要說說渲染合併的問題。既然乙個image就是乙個mesh,然後上面有單獨的貼圖顯示。那麼是不是就代表著,我乙個介面裡有有100個ui元件,那麼就會產生100個mesh,產生100個drawcall呢?

從原理上的確是會這樣,但實際執行的時候,並沒有產生這樣的結果。這裡unity引入了乙個canvas(畫布)的概念。在預設建立的ugui根節點上面,你可以看到這個canvas

它定義了一些東西,比如當前畫布的渲染模式、排序情況。

然後當你往這個canvas裡面新增其他ui元件的時候,這些元件實際上就是被這個canvas統和在一起了。比如,同乙個canvas裡面有100個image,canvas將會把100個單獨的mesh合併成乙個大的sharemesh,用於渲染。如果剛好這100個image都是使用了相同的或者是同乙個圖集裡面的,那麼由於使用的mesh只有乙個(sharemesh),材質球都是同乙個(內建的預設材質球),貼圖也是同一張,所以得到的結果就是,drawcall只有乙個。

這實際上就是ugui最基本的優化思路了。合併sharemesh、合併圖集,減少drawcall。但在實際專案的操作中,會遇到很多問題。

就拿sharemesh來說,我們可以把場景的靜態合併拿來對比一下。這個sharemesh和場景合併成combinedmesh是一樣的,把很多小模型合成乙個大模型。但可以想象,場景的模型合併是靜態的,也就是說它是擺在那裡不會動的。大部分情況下,ui也是不會動的,在這種情況下,合併sharemesh沒有太大的問題。但還是會有很多會動的時候。比如說,arpg很喜歡在角色頭頂上顯示角色名字,名字會跟隨著角色移動。再比如,聊天裡面不停的刷各種文字聊天資訊,或者你需要乙個圖示從左邊飛到右邊的動畫,或者你的血量條需要動態變化長度之類,等等。這些情況下,由於單個顯示元素的mesh 形狀發生變化(文字的改變實際上也是改變了mesh形狀),如果原來這些ui元素就是和其他所有ui元素合併同乙個sharemesh的,那麼問題就來了,假如你ui元素非常多,產生了乙個2m的sharemesh(這是乙個比較極端的例子,實際上一般的sharemesh都是幾百k,出現這麼大的sharemesh本身就需要注意了),由於乙個小頂點發生了改變,導致2m的整體sharemesh也需要重新的生成頂點資訊,並且整個sharemesh重新渲染,這明顯是得不償失的。你會發現在ui動畫的過程中,整個遊戲都在卡。卡的是cpu方面的實時合併網格頂點,和渲染那邊的整個大mesh重新繪製。

那麼怎麼解決這個問題呢?思路也像場景模型和角色模型的關係一樣。首先要知道的是,在根節點下面掛了canvas之後,還是可以在子節點上面繼續的掛canvas的。這樣,從理論上說,unity會找到最小單位的canvas作合併。既然大部分的ui是不會動的,那麼我們可以做一些分層,把不會動的元素放在同一層,然後會動的元素,根據動的頻率和型別,分別在他們的子節點上面掛上canvas。這樣就可以在保證大部分ui元素能合併渲染的基礎上,減少整體重繪的範圍。

聽完上面的一些關於canvas的說法之後,你是不是有種想法,那麼那些會動的東西要不掛多幾層canvas,讓它控制得更細膩?當初我也是這樣想的。不過後來發現這樣做很有問題。canvas存在乙個重繪的問題。如果你把乙個canvas的節點setactive為false之後,再在它身上做改變座標之類的操作,再把canvas節點setactive為true,這時候canvas渲染的還是之前的畫面,並沒有把改變的東西渲染到。在這個時候,要麼canvas裡面的元素稍微變化,讓它被動的觸發重繪,要麼呼叫forceupdate的api讓canvas主動重繪。但這樣的操作如果在canvas巢狀了很多層之後,是很難去控制的,也不可能每次都強制主動重繪canvas,畢竟很多時候是沒必要這樣做的。最重要的一點是,編寫ui框架的時候,不應該要以後寫業務邏輯的人去關心業務上導致ui顯示發生改變後,再去做些什麼樣的潛規則才能正常顯示出來。所以我建議canvas也不要分得太多,特別不要巢狀得太多層次,導致失去控制了。

說完canvas的sharemesh合併網格,最後再說一下圖集的問題。如果遊戲內容比較多,是不太可能把所有的ui都合併在同乙個圖集裡面的。所以必然會產生多個圖集的問題。而使用同乙個圖集的image能合併成乙個drawcall的前提,是他們中間沒有再穿插著使用了其他圖集的image。這裡指的穿插,也包括矩形邊緣的穿插。所以你可能看到兩張圖好像沒有疊在一起,但也有可能因為透明區域的原因產生了疊加。

所以在設計ui的時候,最好避免太多層疊在一起的設計。邊界也盡量的分明。

雖然很多剛入門unity的人,特別是程式設計師,在進公司之後都會被安排為做ui功能。但我一直不認為ui就是乙個這麼簡單的工作。要懂得乙個ui系統的潛規則,做好效能優化,讓自己做出的介面比別人的好看又流暢,我一直都覺得是乙個很困難的主題。

對於ui美術來說,如果不懂這些優化的原理,在設計ui結構的時候可能就會產生很多程式設計師也無法去解決的問題,最後只能要麼效能差,要麼大改方案重新做。我覺得懂得思考這些問題之後,才算是乙個合格的遊戲ui設計師,而不僅僅是會使用photoshop,畫出效果。

一些 Mysql 的優化經驗

一些 mysql 的 優化經驗 從資料庫結構做起 字段型別的定義時遵循以下規則 選用字段長度最小 優先使用定長型 盡可能的定義 not null 數值型字段中避免使用 zerofill 如果要儲存的資料為字串,且可能值已知且有限,優先使用 enum 或 set 索引的優化至關重要 以下如果沒有特殊說...

Android 效能優化的一些方法

2.view中設定快取屬性.setdrawingcache為true.3.優化你的布局。通過android sdk中tools目錄下的layoutopt 命令檢視你的布局是否需要優化。4.動態載入view.採用viewstub 避免一些不經常的檢視長期握住引用.5.將acitivity 中的wind...

Android效能優化的一些方法

android4.0這個選項是預設開啟的。2.view中設定快取屬性.setdrawingcache為true.3.優化你的布局。通過android sdk中tools目錄下的layoutopt 命令檢視你的布局是否需要優化。4.動態載入view.採用viewstub 避免一些不經常的檢視長期握住引...