Q1:UGUI裡的這個選項 ,應該是ETC2拆分Alpha通道的意思,但是在使用中并沒起作用?請問有沒有什麼拆分的标準和特别要求呢?

據我們所知,alpha split 的功能最初隻對 Unity 2D 的 Sprite(SpriteRenderer)有完整的支援,而UI的支援是在Unity 5.4版本之後的。建議大家在Unity 5.4版本以後的UGUI中嘗試該功能。
Q2:在UI界面中,用Canvas還是用RectTransform做根節點更好?哪種方法效率更高?
Canvas劃分是個很大的話題。簡單來說,因為一個Canvas下的所有UI元素都是合在一個Mesh中的,過大的Mesh在更新時開銷很大,是以一般建議每個較複雜的UI界面,都自成一個Canvas(可以是子Canvas),在UI界面很複雜時,甚至要劃分更多的子Canvas。同時還要注意動态元素和靜态元素的分離,因為動态元素會導緻Canvas的mesh的更新。最後,Canvas又不能細分的太多,因為會導緻Draw Call的上升。我們後續将對UI子產品做具體的講解,盡請期待。
Q3:UWA性能檢測報告中的Shared UI Mesh表示什麼呢?
Shared UI Mesh是在Unity 5.2 版本後UGUI系統維護的UI Mesh。在以前的版本中,UGUI會為每一個Canvas維護一個Mesh(名為BatchedMesh,其中再按材質分為不同的SubMesh)。而在Unity 5.2版本後,UGUI底層引入了多線程機制,而其Mesh的維護也發生了改變,目前Shared UI Mesh作為靜态全局變量,由底層直接維護,其大小與目前場景中所有激活的UI元素所生成的網格數相關。 一般來說當界面上UI元素較多,或者文字較多時該值都會較高,在使用UI/Effect/shadow和UI/Effect/Outline時需要注意該值,因為這兩個Effect會明顯增加文字所帶來的網格數。
Q4:在使用NGUI時,我們通常會将很多小圖打成一個大的圖集,以優化記憶體和Draw Call。而在UGUI時代,UI所使用的Image必須是Sprite;Unity提供了SpritePacker。 它的工作流程和UGUI Atlas Paker有較大的差别。在Unity Asset中,我們壓根看不到圖集的存在。 問題是:
1. SpritePacker大概的工作機制是什麼樣的?
2. 如果Sprite沒有打包成AssetBundle,直接在GameObject上引用,那麼在Build時Unity會将分散的Sprite拼接在一起麼?如果沒有拼接,那SpritePacker是不是隻會優化Draw Call,記憶體占用上和不用SpritePacker的分離圖效果一樣?
3. 如果将Sprite打成AssetBundle,AssetBundle中的資源是分散的Sprite嗎?如果不是,不同的AssetBundle中引用了兩張Sprite,這兩張Sprite恰好用SpritePacker拼在了一起,是不是就會存在兩份拼接的Sprite集?
4. 如果想使用NGUI Atlas Packer的工作流程,該如何去實作?
簡單來說,UGUI和 NGUI 類似,但是更加自動化。隻需要通過設定 Packing Tag 即可指定哪些 Sprite 放在同一個 Atlas 下。 可以通過 Edit -> Project Settings -> Editor -> Sprite Packer 的 Mode 來設定是否起效,何時起效(一種是進入 Play Mode 就生效,一種是 Build 時才生效)。是以隻要不選 Disabled,Build 時就會把分散的 Sprite 拼起來。 可以認為 Sprite 就是一個殼子,實際上本身不包含紋理資源,是以打包的時候會把Atlas 打進去。如果不用依賴打包,那麼分開打兩個 Sprite 就意味各自的AssetBundle 裡都會有一個 Atlas。 可以通過第三方工具(如 Texture Packer)制作 Atlas,導出 Sprite 資訊(如,第 N 個 Sprite 的 Offset 和 Width,Height 等),然後在 Unity 中通過腳本将該 Atlas 轉成一個 Multiple Mode 的 Sprite 紋理(即一張紋理上包含了多個 Sprite),同時禁用 Unity 的 Sprite Packer 即可。 兩種做法各有利弊,建議分析一下兩種做法對于自身項目的合适程度來進行選擇。
Q5:在Unity 5.x版本下,我們在用UGUI的過程中發現它把圖集都打進了包裡,這樣就不能自動更新了,請問圖集怎麼做自動更新呢?
在Unity 5.x中UGUI使用的Atlas确實是不可見的,是以無法直接将其獨立打包。但我們建議,可以把Packing Tag相同的源紋理檔案,打到同一個AssetBundle中(設定一樣的AssetBundle Name),進而避免Atlas的備援。同時這樣打包可以讓依賴它的Canvas的打包更加自由,即不需要把依賴它的Canvas都打在一個AssetBundle中,在更新時直接更新Atlas所在的AssetBundle即可。
Q6:ScrollRect在滾動的時候,會産生Canvas.SendwillRenderCanvases,有辦法消除嗎?
ScrollRect在滾動時,會産生OnTransformChanged的開銷,這是UI元素在移動時觸發的,但通常這不會觸發Canvas.SendWillRenderCanvases。 如果觀察到Canvas.SendWillRenderCanvases耗時較高,可以檢查下ScrollRect所在的Canvas是否開啟了Pixel Perfect的選項,該選項的開啟會導緻UI元素在發生位移時,其長寬會被進行微調(為了對其像素),而ScrollRect中通常有較多的UI元素,進而産生較高的Canvas.SendWillRenderCanvases開銷。是以可以嘗試關閉Pixel Perfect看效果是否可以接受,或者嘗試在滾動過程中暫時關閉Pixel Perfect等方式來消除其開銷。
Q1:我在UGUI裡更改了Image的Color屬性,那麼Canvas是否會重建?我隻想借用它的Color做Animation裡的變化量。
如果修改的是Image元件上的Color屬性,其原理是修改頂點色,是以是會引起網格的Rebuild的(即Canvas.BuildBatch操作,同時也會有Canvas.SendWillRenderCanvases的開銷)。而通過修改頂點色來實作UI元素變色的好處在于,修改頂點色可以保證其材質不變,是以不會産生額外的Draw Call。
Q2:Unity自帶的UI Shader處理顔色時,改_Color屬性不會觸發頂點重建嗎?
在UI的預設Shader中存在一個Tint Color的變量,正常情況下,該值為常數(1,1,1),且并不會被修改。如果是用腳本通路Image的Material,并修改其上的Tint Color屬性時,對UI元素産生的網格資訊并沒有影響,是以就不會引起網格的Rebuild。但這樣做因為修改了材質,是以會增加一個Draw Call。
Q3:能否就UGUI Batch提出一些建議呢?是否有一些Batch的規則?
在 UGUI 中,Batch是以Canvas為機關的,即在同一個Canvas下的UI元素最終都會被Batch到同一個Mesh中。而在Batch前,UGUI會根據這些UI元素的材質(通常就是Atlas)以及渲染順序進行重排,在不改變渲染結果的前提下,盡可能将相同材質的UI元素合并在同一個SubMesh中,進而把DrawCall降到最低。而Batch的操作隻會在UI元素發生變化時才進行,且合成的Mesh越大,操作的耗時也就越大。 是以,我們建議盡可能把頻繁變化(位置,顔色,長寬等)的UI元素從複雜的Canvas中分離出來,進而避免複雜的Canvas頻繁重建。
Q4:我用的是UGUI Canvas,Unity 5.3.4版本,請問如何檢視每次Rebuild Batch影響的頂點數, Memory Profiler是個辦法但是不好定位。
由于Unity引擎在5.2後開始使用Shared UI Mesh來存儲UI Mesh,是以确實很難檢視每次Rebuild的UI頂點數。但是,研發團隊可以嘗試通過Frame Debugger工具對UI界面進行進一步的檢視。
Q5:動靜分離或者多Canvas帶來性能提升的理論基礎是什麼呢?如果靜态部分不變動,整個Canvas就不重新整理了?
在UGUI中,網格的更新或重建(為了盡可能合并UI部分的DrawCall)是以Canvas為機關的,且隻在其中的UI元素發生變動(位置、顔色等)時才會進行。是以,将動态UI元素與靜态UI元素分離後,可以将動态UI元素的變化所引起的網格更新或重建所涉及到的範圍變小,進而降低一定的開銷。而靜态UI元素所在的Canvas則不會出現網格更新和重建的開銷。
Q6:UWA建議“盡可能将靜态UI元素和頻繁變化的動态UI元素分開,存放于不同的Panel下。同時,對于不同頻率的動态元素也建議存放于不同的Panel中。”那麼請問,如果把特效放在Panel裡面,需要把特效拆到動态的裡面嗎?
通常特效是指粒子系統,而粒子系統的渲染和UI是獨立的,僅能通過Render Order來改變兩者的渲染順序,而粒子系統的變化并不會引起UI部分的重建,是以特效的放置并沒有特殊的要求。
Q7:多人同屏的時候,人物移動會使得頭頂上的名字Mesh重組,進而導緻較為嚴重的卡頓,請問一下是否有優化的辦法?
如果是用UGUI開發的,當頭頂文字數量較多時,确實很容易引起性能問題,可以考慮從以下幾點入手進行優化: 盡可能避免使用UI/Effect,特别是Outline,會使得文本的Mesh增加4倍,導緻UI重建開銷明顯增大; 拆分Canvas,将螢幕中所有的頭頂文字進行分組,放在不同的Canvas下,一方面可以降低更新的頻率(如果分組中沒有文字移動,該組就不會重建),另一方面可以減小重建時涉及到的Mesh大小(重建是以Canvas為機關進行的); 降低移動中的文字的更新頻率,可以考慮在文字移動的距離超過一個門檻值時才真正進行位移,進而可以從機率上降低Canvas更新的頻率。
Q1:遊戲中出現UI界面重疊,該怎麼處理較好?比如目前有一個全屏顯示的UI界面,點其中一個按鈕會再起一個全屏界面,并把第一個UI界面蓋住。我現在的做法是把被覆寫的界面 SetActive(False),但發現後續 SetActive(True) 的時候會有 GC.Alloc 産生。這種情況下,希望既降低 Batches 又降低 GC Alloc 的話,有什麼推薦的方案嗎?
可以嘗試通過添加一個 Layer 如 OutUI, 且在 Camera 的 Culling Mask 中将其取消勾選(即不渲染該 Layer)。進而在 UI 界面切換時,直接通過修改 Canvas 的 Layer 來實作“隐藏”。但需要注意事件的屏蔽,禁用動态的 UI 元素等等。 這種做法的優點在于切換時基本沒有開銷,也不會産生多餘的 Draw Call,但缺點在于“隐藏時”依然還會有一定的持續開銷(通常不太大),而其對應的 Mesh 也會始終存在于記憶體中(通常也不太大)。 以上的方式可供參考,而性能影響依舊是需要視具體情況而定。
Q2:如圖,我們在UI打開或者移動到某處的時候經常會觀測到CPU上的沖激,經過進一步觀察發現是因為Instantiate産生了大量的GC。想請問下Instantiate是否應該産生GC呢?我們能否通過資源制作上的調整來避免這樣的GC呢?如下圖,因為一次性産生若幹MB的GC在直覺感受上還是很可觀的。
準确的說這些 GC Alloc 并不是由Instantiate 直接引起的,而是因為被執行個體化出來的元件會進行 OnEnable 操作,而在 OnEnable 操作中産生了 GC,比如以上圖中的函數為例: 上圖中的 Text.OnEnable 是在執行個體化一個 UI 界面時,UI 中的文本(即 Text 元件)進行了 OnEnable 操作,其中主要是初始化文本網格的資訊(每個文字所在的網格頂點,UV,頂點色等等屬性),而這些資訊都是儲存在數組中(即堆記憶體中),是以文本越多,堆記憶體開銷越大。但這是不可避免的,隻能盡量減少出現次數。 是以,我們不建議通過 Instantiate/Destroy 來處理切換頻繁的 UI 界面,而是通過 SetActive(true/false),甚至是直接移動 UI 的方式,以避免反複地造成堆記憶體開銷。
Q1:UGUI的圖集操作中我們有這麼一個問題,加載完一張圖集後,使用這個方式擷取其中一張圖的資訊:assetBundle.Load (subFile, typeof (Sprite)) as Sprite; 這樣會複制出一個新貼圖(圖集中的子圖),不知道有什麼辦法可以不用複制新的子圖,而是直接使用圖集資源 。
經過測試,這确實是 Unity 在 4.x 版本中的一個缺陷,理論上這張“新貼圖(圖集中的子圖)”是不需要的,并不應該加載。 是以,我們建議通過以下方法來繞過該問題: 在 assetBundle.Load (subFile, typeof (Sprite)) as Sprite; 之後,調用 Texture2D t = assetBundle.Load (subFile, typeof (Texture2D)) as Texture2D; Resources.UnloadAsset(t); 進而解除安裝這部分多餘的記憶體。
Q2:加載UI預制的時候,如果把特效放到預制裡,會導緻加載非常耗時。怎麼優化這個加載時間呢?
UI和特效(粒子系統)的加載開銷在多數項目中都占據較高的CPU耗時。UI界面的執行個體化和加載耗時主要由以下幾個方面構成: 紋理資源加載耗時 UI界面加載的主要耗時開銷,因為在其資源加載過程中,時常伴有大量較大分辨率的Atlas紋理加載,我們在之前的Unity加載子產品深度分析之紋理篇有詳細講解。對此,我們建議研發團隊在美術品質允許的情況下,盡可能對UI紋理進行簡化,進而加快UI界面的加載效率。UI網格重建耗時 UI界面在執行個體化或Active時,往往會造成Canvas(UGUI)或Panel(NGUI)中UIDrawCall的變化,進而觸發網格重建操作。當Canvas或Panel中網格量較大時,其重建開銷也會随之較大。 UI相關構造函數和初始化操作開銷 這部分是指UI底層類在執行個體化時的ctor開銷,以及OnEnable和OnDisable的自身開銷。 上述2和3主要為引擎或插件的自身邏輯開銷,是以,我們應該盡可能避免或降低這兩個操作的發生頻率。我們的建議如下: 在記憶體允許的情況下,對于UI界面進行緩存。盡可能減少UI界面相關資源的重複加載以及相關類的重複初始化; 根據UI界面的使用頻率,使用更為合适的切換方式。比如移進移出或使用Culling Layer來實作UI界面的切換效果等,進而降低UI界面的加載耗時,提升切換的流暢度。 對于特效(特别是粒子特效)來說,我們暫時并沒有發現将UI界面和特效耦合在一起,其加載耗時會大于二者分别加載的耗時總和。是以,我們僅從優化粒子系統加載效率的角度來回答這個問題。粒子系統的加載開銷,就目前來看,主要和其本身元件的反序列化耗時和加載數量相關。對于反序列化耗時而言,這是Unity引擎負責粒子系統的自身加載開銷,開發者可以控制的空間并不大。對于加載數量,則是開發者需要密切關注的,因為在我們目前看到的項目中,不少都存在大量的粒子系統加載,有些項目的數量甚至超過1000個,如下圖所示。是以,建議研發團隊密切關注自身項目中粒子系統的數量使用情況。一般來說,建議我們建議粒子系統使用數量的峰值控制在400以下。![]()
關于Unity中的UGUI優化,你可能遇到這些問題 ![]()
關于Unity中的UGUI優化,你可能遇到這些問題
Q3:我有一個UI預設,它使用了一個圖集, 我在打包的時候把圖集和UI一起打成了AssetBundle。我在加載生成了GameObject後立刻解除安裝了AssetBundle對象, 但是當我後面再銷毀GameObject的時候發現圖集依然存在,這是什麼情況呢?
這是很可能出現的。unload(false)解除安裝AssetBundle并不會銷毀其加載的資源 ,是必須對其調用Resources.UnloadAsset,或者調用Resources.UnloadUnusedAssets才行。關于AssetBundle加載的詳細解釋可以參考我們之前的文章:你應該知道的AssetBundle管理機制。
Q1:我在用Profiler真機檢視iPhone App時,發現第一次打開某些UI時,Font.CacheFontForText占用時間超過2s,這塊主要是由什麼影響的?若iPhone5在這個接口消耗2s多,是不是問題很大?這個消耗和已經生成的RenderTexture的大小有關嗎?
Font.CacheFontForText主要是指生成動态字型Font Texture的開銷, 一次性打開UI界面中的文字越多,其開銷越大。如果該項占用時間超過2s,那麼确實是挺大的,這個消耗也與已經生成的Font Texture有關系。簡單來說,它主要是看目前Font Texture中是否有地方可以容下接下來的文字,如果容不下才會進行一步擴大Font Texture,進而造成了性能開銷。
原文出處:侑虎科技
本文作者:admin
轉載請與作者聯系,同時請務必标明文章原始出處和原文連結及本聲明。