?? 開發(fā)過程中對UGUI的一個小總結(jié)房揭。
?? 首先從原畫師拿到效果圖棚瘟,美術切圖,拿到碎圖后打成大圖吟孙。
? ? 我們先來說一下圖:RGBA8888:每一個通道占8位。大圖:1024*1024聚蝶。高端??? 機:2048*2048杰妓。
?? 我們通常從美工那里拿來碎圖,歪歪使用的一個工具texturepacker 把碎圖打成大圖碘勉,導出成 .tpsheet? .png格式稚失。其次我們要做的是,在Unity3d中導入插件texture import(此插件會自動把大圖打成圖集).然后把大圖導入U3D恰聘。
? 在UI優(yōu)化中較為明顯而眾所周知的就是降低DrawCall(注釋:DrawCall CPU向GPU發(fā)送的一次渲染指令)。而降低DrawCall我們會采用靜態(tài)合批和動態(tài)合批吸占。(注釋:合批必須同類型 Mesh 同材質(zhì)晴叨。)DrawCall的標準 RGP 類 小于 150。
?? 然而矾屯,工作中我們優(yōu)化大概分為UI層級計算兼蕊,UI重建和多層級渲染。接下來我們來說一下:
UI層級 計算 :
1件蚕,計算層級:
1,如果有一個UI元素孙技,它所占的屏幕范圍內(nèi)(通常是矩形)产禾,如果沒有任何UI在它的底下,那么它的層級號就是0(最底下)牵啦;
2,如果有一個UI在其底下且該UI可以和它Batch亚情,那它的層級號與底下的UI層級一樣;
3,如果有一個UI在其底下但是無法與它Batch哈雏,那它的層級號為底下的UI的層級+1楞件;
4,如果有多個UI都在其下面,那么按前兩種方式遍歷計算所有的層級號裳瘪,其中最大的那個作為自己的層級號土浸。
合并批次原則;
同層級{同材質(zhì)球,}
{0 : { image ??, image, text ?}}
1,Unity會將每一層的所有元素進行一個排序(按照材質(zhì)彭羹、紋理等信息)黄伊,合并掉可以Batch的元素成為一個批次
2,Text組件會排在Image組件之前渲染。
3,派殷,Unity會再做一個優(yōu)化还最,即如果相鄰間的兩個批次正好可以Batch的話就會進行Batch(這么處理,可以合成一個批次愈腾,如下:)
{0 : { text ??image ?}}textàimage
{1 : { text ?,image }}
0: textàimage ???1: textàimage
下面我們來看一個列子憋活,這樣會更加的清晰:
一個層級為0的ImageA,一個層級為1的ImageB(2個Image可Batch)和一個層級為0的TextC虱黄,
textCàimageAàimageB
一個層級為0的TextD悦即,一個層級為1的TextE(2個Text可Batch)和一個層級為0的ImageF,
0 : textD –>imageFà1 : textEàimage h ???3:
0 : textD –>imageFà1 : textEàimage h
總結(jié):
1,有相同材質(zhì)和紋理的UI元素是可以Batch的橱乱,可以Batch的UI上下疊在一塊不會影響性能辜梳,但是如果不能Batch的UI元素疊在一塊昵济,就會增加Drawcall開銷
2,盡量讓同一個材質(zhì)球上的東西 放在同一級上
3,有些情況可以考慮人為增加層級從而減少Drawcall乏沸,比如一個Text的層級為0,另一個可Batch的Text疊在一個圖片A上桐愉,層級為1危纫,那此時2個Text因為層級不同會安排2個Drawcall宗挥,但如果在第一個Text下放一個透明的圖片(與圖片A可Batch),那兩個Text的層級就一致了种蝶,Drawcall就可以減少一個契耿。
Text0 –》Imageàtext 1
4,應該盡量避免使用Mask,其實Mask的功能有些時候可以變通實現(xiàn)螃征,比如設計一個邊框搪桂,讓這個邊框疊在最上面,底下的UI移動時盯滚,就會被這個邊框遮滋咝怠酗电;
5, z值 保持為0
UI重建:
1,動靜分離:
經(jīng)常發(fā)生變動的ui單獨放在一個Canvase
2内列,刪除不必要的元素撵术。
3,CanvasRender.setDisable .
4德绿,Text的Best Fit選項.
5,Canvas的Pixel Perfect選項
6,使用緩存池來保存ScrollView中的Item,對于移出或移進View外的的元素荷荤,不要調(diào)用disable或enable,而是把它們放到緩存池里或從緩存池中取出復用。
7, 除了rebuild過程之外移稳,UGUI的touch處理消耗也可能會成為性能熱點蕴纳。因為UGUI在默認情況下會對所有可見的Graphic組件調(diào)用raycast。對于不需要接收touch事件的grahic个粱,一定要禁用raycast古毛。對于unity5以上的可以關閉graphic的Raycast Target而對于unity4.6,可以給不需要接收touch的UI元素加上canvasgroup組件都许。
多層級渲染:
1,canvasRender .setdisable()
2,只掛載button替代 ?空的image
3稻薇,不要使用空的Image,在Unity中,RayCast使用Graphi作為基本元素來檢測touch,在筆者參與的項目中胶征,很多同學使用空的image并將alpha設置為0來接收touch事件塞椎,這樣會產(chǎn)生不必要的overdraw。通過如下類NoDrawingRayCast來接收事件可以避免不必要的overdraw睛低。
3. public class NoDrawingRayCast : Graphic
4. {
5. ? ? public override void SetMaterialDirty()
6. ? ? {
7. ? ? }
8. ? ? public override void SetVerticesDirty()
9. ? ? {
10. ? ? }
11. ? ? protected override void OnFillVBO(List vbo)
12. ? ? {
13. ? ? ? ? vbo.Cslear();
14. ? ? }
}
性能檢測工具:
1profile
常見問題:
Q1:我在UGUI里更改了Image的Color屬性案狠,那么Canvas是否會重建?我只想借用它的Color做Animation里的變化量钱雷。
如果修改的是Image組件上的Color屬性骂铁,其原理是修改頂點色,因此是會引起網(wǎng)格的Rebuild的(即Canvas.BuildBatch操作罩抗,同時也會有Canvas.SendWillRenderCanvases的開銷)拉庵。而通過修改頂點色來實現(xiàn)UI元素變色的好處在于,修改頂點色可以保證其材質(zhì)不變套蒂,因此不會產(chǎn)生額外的Draw Call钞支。
Q2:Unity自帶的UI Shader處理顏色時,改_Color屬性不會觸發(fā)頂點重建嗎?
在UI的默認Shader中存在一個Tint Color的變量操刀,正常情況下伸辟,該值為常數(shù)(1,1,1),且并不會被修改馍刮。如果是用腳本訪問Image的Material,并修改其上的Tint Color屬性時窃蹋,對UI元素產(chǎn)生的網(wǎng)格信息并沒有影響卡啰,因此就不會引起網(wǎng)格的Rebuild静稻。但這樣做因為修改了材質(zhì),所以會增加一個Draw Call匈辱。
Q:動靜分離或者多Canvas帶來性能提升的理論基礎是什么呢振湾?如果靜態(tài)部分不變動,整個Canvas就不刷新了亡脸?
在UGUI中押搪,網(wǎng)格的更新或重建(為了盡可能合并UI部分的DrawCall)是以Canvas為單位的,且只在其中的UI元素發(fā)生變動(位置浅碾、顏色等)時才會進行大州。因此,將動態(tài)UI元素與靜態(tài)UI元素分離后垂谢,可以將動態(tài)UI元素的變化所引起的網(wǎng)格更新或重建所涉及到的范圍變小厦画,從而降低一定的開銷。而靜態(tài)UI元素所在的Canvas則不會出現(xiàn)網(wǎng)格更新和重建的開銷滥朱。
Q:UWA建議“盡可能將靜態(tài)UI元素和頻繁變化的動態(tài)UI元素分開根暑,存放于不同的Panel下。同時徙邻,對于不同頻率的動態(tài)元素也建議存放于不同的Panel中排嫌。”那么請問缰犁,如果把特效放在Panel里面淳地,需要把特效拆到動態(tài)的里面嗎?
通常特效是指粒子系統(tǒng)民鼓,而粒子系統(tǒng)的渲染和UI是獨立的薇芝,僅能通過Render Order來改變兩者的渲染順序,而粒子系統(tǒng)的變化并不會引起UI部分的重建丰嘉,因此特效的放置并沒有特殊的要求夯到。
Q:多人同屏的時候,人物移動會使得頭頂上的名字Mesh重組饮亏,從而導致較為嚴重的卡頓耍贾,請問一下是否有優(yōu)化的辦法?
如果是用UGUI開發(fā)的路幸,當頭頂文字數(shù)量較多時荐开,確實很容易引起性能問題,可以考慮從以下幾點入手進行優(yōu)化:
1.盡可能避免使用UI/Effect简肴,特別是Outline晃听,會使得文本的Mesh增加4倍,導致UI重建開銷明顯增大;
2.拆分Canvas能扒,將屏幕中所有的頭頂文字進行分組佣渴,放在不同的Canvas下,一方面可以降低更新的頻率(如果分組中沒有文字移動初斑,該組就不會重建)辛润,另一方面可以減小重建時涉及到的Mesh大小(重建是以Canvas為單位進行的)见秤;
3.降低移動中的文字的更新頻率砂竖,可以考慮在文字移動的距離超過一個閾值時才真正進行位移,從而可以從概率上降低Canvas更新的頻率鹃答。
三乎澄、界面切換
Q1:游戲中出現(xiàn)UI界面重疊,該怎么處理較好挣跋?比如當前有一個全屏顯示的UI界面三圆,點其中一個按鈕會再起一個全屏界面,并把第一個UI界面蓋住避咆。我現(xiàn)在的做法是把被覆蓋的界面SetActive(False)舟肉,但發(fā)現(xiàn)后續(xù)SetActive(True)的時候會有GC.Alloc產(chǎn)生。這種情況下查库,希望既降低Batches又降低GC Alloc的話路媚,有什么推薦的方案嗎?
可以嘗試通過添加一個Layer如OutUI樊销, 且在Camera的Culling Mask中將其取消勾選(即不渲染該Layer)整慎。從而在UI界面切換時,直接通過修改Canvas的Layer來實現(xiàn)“隱藏”围苫。但需要注意事件的屏蔽裤园,禁用動態(tài)的UI元素等等。
這種做法的優(yōu)點在于切換時基本沒有開銷剂府,也不會產(chǎn)生多余的Draw Call拧揽,但缺點在于“隱藏時”依然還會有一定的持續(xù)開銷(通常不太大),而其對應的Mesh也會始終存在于內(nèi)存中(通常也不太大)腺占。
以上的方式可供參考淤袜,而性能影響依舊是需要視具體情況而定。
Q2:通過移動位置來隱藏UI界面衰伯,會使得被隱藏的UIPanel繼續(xù)執(zhí)行更新(LateUpdate有持續(xù)開銷)铡羡,那么如果打開的界面比較多,CPU的持續(xù)開銷是否就會超過一次SetActive所帶來的開銷意鲸?
這確實是需要注意的烦周,通過移動的方式“隱藏”的UI界面只適用于幾個切換頻率最高的界面尽爆,另外,如果“隱藏”的界面持續(xù)開銷較高读慎,可以考慮只把一部分Disable教翩,這個可能就需要具體看界面的復雜度了。一般來說在沒有UI元素變化的情況下贪壳,持續(xù)的Update開銷是不太明顯的。
Q3:如圖蚜退,我們在UI打開或者移動到某處的時候經(jīng)常會觀測到CPU上的沖激闰靴,經(jīng)過進一步觀察發(fā)現(xiàn)是因為Instantiate產(chǎn)生了大量的GC。想請問下Instantiate是否應該產(chǎn)生GC呢钻注?我們能否通過資源制作上的調(diào)整來避免這樣的GC呢蚂且?如下圖,因為一次性產(chǎn)生若干MB的GC在直觀感受上還是很可觀的幅恋。
準確的說這些GC Alloc并不是由Instantiate直接引起的杏死,而是因為被實例化出來的組件會進行OnEnable操作,而在OnEnable操作中產(chǎn)生了GC捆交,比如以上圖中的函數(shù)為例:
上圖中的Text.OnEnable是在實例化一個UI界面時淑翼,UI中的文本(即Text組件)進行了OnEnable操作,其中主要是初始化文本網(wǎng)格的信息(每個文字所在的網(wǎng)格頂點品追,UV玄括,頂點色等等屬性),而這些信息都是儲存在數(shù)組中(即堆內(nèi)存中)肉瓦,所以文本越多遭京,堆內(nèi)存開銷越大。但這是不可避免的泞莉,只能盡量減少出現(xiàn)次數(shù)哪雕。
因此,我們不建議通過Instantiate/Destroy來處理切換頻繁的UI界面鲫趁,而是通過SetActive(true/false)斯嚎,甚至是直接移動UI的方式,以避免反復地造成堆內(nèi)存開銷饮寞。
四孝扛、加載相關
Q1:UGUI的圖集操作中我們有這么一個問題,加載完一張圖集后幽崩,使用這個方式獲取其中一張圖的信息:assetBundle.Load (subFile, typeof (Sprite)) as Sprite;這樣會復制出一個新貼圖(圖集中的子圖)苦始,不知道有什么辦法可以不用復制新的子圖,而是直接使用圖集資源 慌申。
經(jīng)過測試陌选,這確實是Unity在4.x版本中的一個缺陷理郑,理論上這張“新貼圖(圖集中的子圖)”是不需要的,并不應該加載咨油。 因此您炉,我們建議通過以下方法來繞過該問題:
在assetBundle.Load (subFile, typeof (Sprite)) as Sprite;之后,調(diào)用
Texture2D t = assetBundle.Load (subFile, typeof (Texture2D)) as Texture2D;
Resources.UnloadAsset(t);
從而卸載這部分多余的內(nèi)存役电。
Q2:加載UI預制的時候赚爵,如果把特效放到預制里,會導致加載非常耗時法瑟。怎么優(yōu)化這個加載時間呢冀膝?
UI和特效(粒子系統(tǒng))的加載開銷在多數(shù)項目中都占據(jù)較高的CPU耗時。UI界面的實例化和加載耗時主要由以下幾個方面構成:
紋理資源加載耗時
UI界面加載的主要耗時開銷霎挟,因為在其資源加載過程中窝剖,時常伴有大量較大分辨率的Atlas紋理加載,我們在之前的Unity加載模塊深度分析之紋理篇有詳細講解酥夭。對此赐纱,我們建議研發(fā)團隊在美術質(zhì)量允許
1,1024*768 ???1024*1024
2, 640*480 ?????512*512