1. 程式人生 > >ui優化

ui優化

screen 可選 right 出現 獲取 交互式 orm 同時 get

https://mp.weixin.qq.com/s/mLd5INIVhkBQvbbXVLmDzw

本文將介紹一些提升Unity UI性能的技巧。更多優化技巧,可以觀看下方的視頻,內容是Unity工程師Ian Dundore在Unite Europe 2017的演講《使用Unity性能提升技巧》。

劃分畫布

問題:UI Canvas上有一個或多個元素變化時,會汙染整個畫布。

畫布(Canvas)是Unity UI的基本組件。它會生成網格來呈現放置在畫布上的UI元素,當UI元素變化時,它會重新生成網格並向GPU發起繪圖調用,從而顯示出UI。

生成這些網格會消耗大量性能,需要將UI元素收集到批處理中,從而盡可能減少繪圖調用。因為批處理的生成過程性能消耗較大,通常只在必要時候才重新生成。問題在於,當畫布上有一個或多個元素變化時,必須重新分析整塊畫布,才能得到繪制元素的最優方法。

許多用戶將整個遊戲的UI都放到一塊畫布,上面擺放了成百上千個元素。因此當修改其中一個元素時,會產生持續數毫秒的CPU使用量飆升情況。

解決方案:劃分畫布

每塊畫布上的元素都與其它畫布的元素相隔離,所以我們可以使用工具來切分畫布,從而解決Unity UI的批處理問題。

我們也可以通過嵌套畫布來解決,這樣允許設計師創建大型分層UI,而且不必擔心不同內容出現在多個畫布上。子畫布的內容與父畫布和同級畫布相互隔離,它們會保持自帶幾何體,執行自己的批處理。

當使用子畫布分離畫布時,嘗試根據畫布更新時間來分組。例如:分離動態元素和靜態元素。

Graphic Raycaster的最佳用法

問題:Graphic Raycaster有哪些最佳用法?

Graphic Raycaster組件能夠將輸入內容轉換為UI事件,它會把觸屏輸入轉為事件,然後發送給相關UI元素。每個接收輸入內容的畫布都需要Graphic Raycaster組件,包括子畫布。

盡管該組件名為Graphic Raycaster,但它卻不是個光線投射器,默認情況下它只會測試UI圖形。該組件會獲取特定畫布上輸入信息相關的UI元素集,然後執行交點測試,它會針對Graphic Raycaster的畫布上每個交互式UI元素的RectTransform,檢查輸入事件發生的位置。

解決方案:關閉靜態或非交互式元素的Raycast Target。

例如:有一個帶文字的按鈕,關閉該元素的Raycast Target會直接減少Graphic Raycaster每幀進行的交點測試次數。

技術分享圖片

問題:有時候Graphic Raycaster會充當光線投射器使用。

如果將畫布上的渲染模式設為世界空間攝像機(Worldspace Camera)或屏幕空間攝像機(Screen Space Camera),此時可以設置阻擋遮罩(Blocking Mask)。

阻擋遮罩決定光線投射器是通過2D物理還是3D物理投射光線,從而了解特定物理對象是否阻擋用戶與UI交互。

解決方案:通過2D或3D物理投射光線會消耗不少性能,所以要謹慎使用該功能。

盡量減少Graphic Raycaster的數量,不要將Graphic Raycaster添加到非交互式UI畫布上,因為這樣做無法檢查交互事件。

避免使用Camera.main

問題:世界空間畫布需要了解交互事件來自哪個攝像機。

當設置畫布進行渲染時,不管該畫布是在世界空間還是攝像機的屏幕空間,都可以指定用於為UI中Graphic Raycaster生成交互事件的攝像機。渲染模式為“Screen Space - Camera”的畫布需要使用該設置,該設置名為“Render Camera”。

技術分享圖片

然而在渲染模式為“World Space”的畫布上,該設置是可選的,名為“Event Camera”。

技術分享圖片

如果將世界空間畫布的Event Camera字段留空,這不意味著該畫布不會接收事件。它會使用遊戲的主攝像機。為了確定哪個攝像機是主攝像機,該畫布會訪問Camera.main屬性。

技術分享圖片

根據Unity所使用的代碼路徑,每幀中每有一個Graphic Raycaster和世界空間畫布,該畫布會訪問7到10次Camera.main。每次訪問Camera.main都會調用Object.FindObjectWithTag。這個做法在運行時並不合適。

解決方案:避免使用Camera.main。

緩存攝像機的引用,然後創建系統來跟蹤主攝像機。如果使用世界空間畫布,要指定Event Camera,不要將該屬性留空。如果需要修改Event Camera,編寫代碼來更新Event Camera屬性。

避免使用布局分組

問題:每個影響布局的UI元素都會至少執行一次GetComponents調用。

當修改布局系統的一個或多個子元素時,會使布局變臟。修改後的子元素會使擁有該元素的布局系統(Layout System)無效化。

簡單介紹一下布局系統:布局系統是一組連續的布局分組(Layout Group),它們在布局元素(Layout Element)之上。布局元素不只是名為Layout Element的組件,它們還包括UI圖像、文字和Scroll Rect組件,而且Scroll Rect同時也是布局分組。

回到問題本身,每個使布局變臟的UI元素都會至少執行一次GetComponents調用,該調用會在布局元素父對象上尋找有效的布局分組。找到有效布局分組後,它會繼續遍歷Transform層級,直到停止尋找分組或是到達層級的根部分,無論先滿足哪個條件都會停止尋找過程。因此。每個布局分組會給每個子布局元素的改變過程添加一次GetComponents調用,使嵌套布局分組的性能變差。

解決方案:避免使用布局分組。

使用錨點進行比例布局。在擁有動態元素數量的活躍UI上,考慮編寫代碼來計算布局,僅在需要時運行該代碼,而不是每次發生改變的時候。

巧妙地聚集UI對象

問題:用錯誤的方法聚集UI對象。

通常情況下,用戶通過重置父對象來聚集UI對象,然後再禁用對象,但這樣會造成不必要的汙染。

解決方案:首先禁用對象,然後將其父對象重置為對象池。

這樣操作僅會改變一次原有的層級,但在重置父對象時,要避免二次改變原有的父對象,也不要改變新的層級。如果要從對象池移除對象,首先重置它的父對象,然後更新數據,再啟用該對象。

如何隱藏畫布

問題:如何隱藏畫布?

有時需要隱藏UI元素和畫布,要怎樣高效完成該任務呢?

解決方案:禁用Canvas組件。

禁用Canvas組件會阻止畫布向GPU發起繪圖調用,所以該畫布不再可見。然而,此時該畫布不會丟棄它的頂點緩沖區,它會保留所有網格和頂點,當重新啟用時不會觸發重構過程,它只會重新繪制畫布內容。

此外,禁用Canvas組件不會觸發Canvas層級上性能消耗較大的OnDisable/OnEnable回調。禁用子組件時要小心,註意它是否運行性能消耗較大的每幀代碼。

UI元素上Animator的最佳用法

問題:如何在UI上使用Animator?

Animator每幀都會改變元素,即使動畫中的數值沒有變化。Animator沒有空指令檢查。

解決方案:

只在頻繁變化的動態元素上加入Animator。對於很少變化的元素,或是僅響應事件時才變化的元素,請自行編寫代碼或補間系統,你可以在Asset Store資源商店找到許多補間系統插件。

小結

提升Unity UI性能的技巧就為大家分享到這裏,更多關於Unity的優化技巧請訪問Unity官方中文論壇(UnityChina.cn) !

ui優化