Unity GUI uGUI 使用心得與效能總結

2021-07-07 10:49:36 字數 4552 閱讀 9782

小哈接觸unity3d也有一段時間了,專案組在ui解決方案的選型一直是用的原生的ugui,因此本人也是使用了一段時間的ugui,在ugui的使用方面積累了一些自己的經驗,在此進行乙個記錄與總結。

本文接下來將會對ugui的runtime效能進行著重討論,其它的因素也很多而且很重要,但是一篇文章講清楚一件事就好了,文後會提供ugui的最佳實踐與一些使用技巧,不想看全文的建議直接到最下面看杰倫,啊,不對,是結論。

遊戲中的ui與其它遊戲中的元素本質上是一樣的,相對來說的不同點在於,ui通常是由2d的組合而成,會包含較多的透明元素與漸變元素,而且一般來說會顯示在螢幕的最頂層

因此,從共同點上說,ui的runtime效能消耗也可以劃分為cpu消耗、gpu消耗與記憶體消耗。其中對於每一部分的具體的消耗以及優化,有諸多大神在網路上發表過文章,比如深入淺出聊unity3d專案優化:從draw calls到gc。但是,總有乙個大家都繞不開的點,那就是drawcall,drawcall數量直接影響了遊戲的幀率,解決了drawcall問題,應該算是解決了80%的問題,所以接下來就著重針對ugui的特性講一講ui系統的drawcall。

unity 5.0在drawcall檢視方面有乙個非常有用的工具,frame debugger,通過[window->frame debugger]開啟。

使用該工具時,遊戲會暫停,然後unity會將當前正在執行的一幀的內容快取下來,其中所有drawcall你都可以進行前進與後退操作,從而能夠從drawcall級別分析開銷。所以沒有公升級5.0的小夥伴趕緊公升級啊。

此外,在用fd看ui效能時,有乙個小竅門就是新開乙個空的scene,然後將你的ui prefab拖到該空場景中,此時就不會受場景中其它物體的影響而只顯示ui的drawcall了。

講了那麼多,開始進入正題。

在降低drawcall方面,乙個非常重要的概念就是batch,因為一次drawcall相當於cpu與gpu進行一次溝通的成本,如果cpu能一次多打包一些資訊給gpu,那麼drawcall數量自然就下來了,這個打包傳輸資訊給gpu的過程就叫做batch,批處理。那麼什麼情況下這些資訊可以打包呢?從ugui的角度,如果你的ui中元件的材質與紋理均相同,這幾個元件就可以被batch。在image元件中,材質對應source image,紋理則對應material;在text元件中材質對應font,紋理也是material。以上對應大部分情況適用,在少部分特殊shader下會失效(待深入研究)。

原理是這樣,但是實際用起來還需要一些技巧,遵循unity的一些渲染次序的規則,才能真正的實現效能優化。以下就一一進行討論。

上面有提到source image圖集,所謂的圖集,就是將好多張零碎的2d小通過unity自帶的sprite packer或第三方的texture packer合併到一張大圖,這樣做有幾大好處,

尺寸為2的次冪時,gpu處理起來會快很多,小圖自己是做不到每張圖都是2的次冪的,但打成一張大圖就可以(浪費一點也無所謂);

cpu在傳送資源資訊給gpu時,只需要傳一張大圖就可以了,因為gpu可以在這張圖中的不同區域進行取樣,然後拼出對應的介面。注意,這就是為什麼需要用同乙個source image圖集的原因,是batch的關鍵,因為乙個drawcall就把所有原材料傳過去了,gpu你畫去吧。

但是顯然把所有打成一張圖集是不合理的,因為這張圖可能非常大,所以就要按照一定規則將進行分類。在分類思路上,我們希望做到drawcall盡可能少,同時資源量也盡可能少(多些重用),但這兩者某種程度上是互斥的,所以折衷一下,可以遵循以下思路:

p.s. 如果你用unity自帶的sprite packer去打包圖集,那麼你可能要在執行模式下才能看到效果。

ugui的層疊順序是按照hierarchy中的順序從上往下進行的,也就是越靠上的元件,就會被畫在越底部。所以ui就是這樣一層一層地疊上去畫出來的。當然這樣乙個乙個地畫效率肯定是不能接受的,所以要合併,要batch,unity自身就提供了乙個演算法去決定哪些層應該合併到一起,並以什麼樣的順序進行繪製。所有相鄰層的可batch的ui元素將會在乙個drawcall完成。接下來就來討論一下unity的層級合併與計算演算法。

unity的ui渲染順序的確定有2個步驟,第一步計算每個ui元素的層級號;第二步合併相同層級號中可以batch的元素作為乙個批次,並對批次進行排序;

先從直觀的角度來解釋計算層級號的演算法:如果有乙個ui元素,它所佔的螢幕範圍內(通常是矩形),如果沒有任何ui在它的底下,那麼它的層級號就是0(最底下);如果有乙個ui在其底下且該ui可以和它batch,那它的層級號與底下的ui層級一樣;如果有乙個ui在其底下但是無法與它batch,那它的層級號為底下的ui的層級+1;如果有多個ui都在其下面,那麼按前兩種方式遍歷計算所有的層級號,其中最大的那個作為自己的層級號

這裡也給一下偽**,假設所有ui元素(拋棄層級關係)都按從上往下的順序被裝在乙個list中,那麼每個ui元素對應的層級號計算可以參考以下:

function callayer(list uielelst)

if(uielelst

.count == 0 ) return;

//initial the first ui element as layer 0

uielelst[0].layer = 0;

for(i = 1 ~ uielelst

.count)

else}}

//if not collide with any elements beneath, set layer to 0

if(!iscollidewithelements)

}

有了層級號之後,就要合併批次了,此時,unity會將每一層的所有元素進行乙個排序(按照材質、紋理等資訊),合併掉可以batch的元素成為乙個批次目前已知的排序規則是,text元件會排在image元件之前渲染,而同一類元件的情況下排序規則未知(好像並沒什麼規則)。經過以上排序,就可以得到乙個有序的批次序列了。這時,unity會再做乙個優化,即如果相鄰間的兩個批次正好可以batch的話就會進行batch。舉個栗子,乙個層級為0的imagea,乙個層級為1的imageb(2個image可batch)和乙個層級為0的textc,unity排序後的批次為textc->imagea->imageb,後兩個批次可以合併,所以是2個drawcall。再舉個栗子,乙個層級為0的textd,乙個層級為1的texte(2個text可batch)和乙個層級為0的imagef,unity排序後的批次為textd->imagef->texte,這時就需要3個drawcall了!(是不是有點暈,再回顧下黑體字)

以下的偽**有些偷懶,實在懶得寫排序、合併之類的,一長串也不好讀,幾個步驟列一下,其它諸位看上面那段文字腦補下吧...

function

mergebatch(list uielelst)

);//merge the ui elements with same layer and batch-key as a batch

var batchlst = uielelst.mergesameelementsasbatch();

//make adjacent batches with same batch-key merged

batchlst.mergeadjacentbatches();

return batchlst;

}

根據以上規則,就可以得出一些「擺ui」的技巧:

mask對於ugui效能來說是噩夢一般的存在,因為很可能因為這個東西,導致drawcall數量成倍增長。

mask實現的具體原理是乙個drawcall來建立stencil mask(來做畫素剔除),然後畫所有子ui,再在最後乙個drawcall移掉stencil mask。這頭尾兩個drawcall無法跟其他ui操作進行batch,所以表面上看加個mask就會多2個drawcall,但是,因為mask這種類似「漢堡包式」的渲染順序,所有mask的子節點與其他ui其實已經處在兩個世界了,上面提到的層級合併規則只能分別作用於這兩個世界了,所以很多原本可以合併的ui就無法合併了。

所以,在使用ugui時,有一些建議:

ugui的效能其實涉及到的方面很多,這裡列出來的只是目前能想到的,因為個人能力有限,可能出些紕漏。對於文中的一些建議,這裡整理一下得出一些最佳實踐:

Unity GUI uGUI 使用心得與效能總結

小哈接觸unity3d也有一段時間了,專案組在ui解決方案的選型一直是用的原生的ugui,因此本人也是使用了一段時間的ugui,在ugui的使用方面積累了一些自己的經驗,在此進行乙個記錄與總結。本文接下來將會對ugui的runtime效能進行著重討論,其它的因素也很多而且很重要,但是一篇文章講清楚一...

使用HtmlParser使用心得

最近因工作的需要,需要檢查html那些不合理或則什麼沒有閉合。在網上找了很久都沒有找到比較合適的工具。於是句試著搞搞htmlparser。獲取html的 string getcontentfromurl string url content sb.tostring response.close re...

frameset 使用心得

欲明白本篇 html徹底剖析 之標記分類,請看 標記一覽 也請先明白圍堵標記與空標記的分別,請看 html概念 框架概念 謂框架便是網頁畫面分成幾個框窗,同時取得多個 url。只需要 即可,面所有框架標記需要放在乙個總起的 html 檔,這個檔案只記錄了該框架如何分割 不會顯示任何資料,所以不必放入...