Canvas
在 Unity UI 中,修改單一元素可能會觸發整個 Canvas 的重新整理。這種重新評估與網格生成的過程會對效能造成嚴重影響,特別是在複雜的 UI 設計中。主要原因如下:
- 網格生成成本高:Unity 的 UI 系統會將元素分組為繪製批次(Draw Calls),但每次小變動都需要重新生成批次,導致高資源消耗。
- 過度使用單一 Canvas:將大量 UI 元素集中於一個 Canvas 內,當有微小更新時,會引發顯著的效能尖峰。
解決方式:將 UI 分割到多個 Canvas 是減少效能問題的有效方法。
- 將單一個 Canvas 分割為 巢狀 Canvas(Nested Canvases),這麼做的好處有
- 子 Canvas 與父 Canvas 和兄弟 Canvas 互相隔離,具有獨立的幾何圖形和批次處理功能。
- 有助於組織層次結構化的 UI。
- 單一 Canvas 的變動不會影響其他 Canvas,縮小了網格重新生成的範圍。
- 分割 Canvas 的最佳實踐
- 依刷新頻率分組:
- 靜態元素:將不常變動的 UI 元素(如背景圖片、標題)放在單獨的 Canvas 上。
- 動態元素:將頻繁更新的 UI 元素(如血量條、分數、計時器)分配到不同的 Canvas。
- 保持一致性:
- 確保 Canvas 內的元素共享相同的 Z 值、材質和貼圖,以提高批次處理的效率。
- 避免過度巢狀化:
- 雖然巢狀 Canvas 功能強大,但過度巢狀化會增加維護難度。應針對邏輯分組策略性使用。
- 範例結構:
主 Canvas
:作為 UI 的總容器。HUD Canvas
:顯示血量、分數和小地圖(頻繁更新)。暫停菜單 Canvas
:包含按鈕和靜態菜單元素(很少更新)。背包 Canvas
:用於展示背包內容(僅在打開時更新)。
- 這樣的結構下,當玩家血量在遊戲中變動時,僅
HUD Canvas
會更新,暫停菜單 Canvas
和背包 Canvas
不受影響。這樣的分割方式有效避免了不必要的效能開銷。
- 依刷新頻率分組:
Graphic Raycasters
Graphic Raycaster
是 Unity UI 系統的一部分,負責將玩家的觸控或點擊行為轉換為遊戲可理解的事件。它的主要作用是:
- 事件檢測:確認玩家觸碰的屏幕區域是否對應到某個 UI 元素。
- 信息傳遞:將觸控位置與設定為可互動的 UI 元素匹配,並將事件傳遞給正確的遊戲部分。
- 運作方式:Graphic Raycaster 僅關注 UI 圖形元素(例如按鈕、圖片)。檢查所有設置為可響應觸控的 UI 部件,並確認玩家的觸碰是否在其範圍內。
Graphic Raycaster
產生的問題:
- 效能消耗高:
- 每次觸控時,Graphic Raycaster 都需要遍歷屏幕上的所有可交互 UI 元素。
- 過多的 Graphic Raycaster 或不必要的檢測可能導致資源浪費,尤其是在大型 UI 設計中。
- 非交互元素:
- 並非所有 UI 元素都需要響應觸控事件,例如裝飾性的圖片或靜態文本。對這些元素進行射線檢測會降低效能。
解決方式:
- 移除不必要的
Graphic Raycasters
- 確保僅在需要檢測觸控事件的 UI Canvas 或元素上添加 Graphic Raycaster。
- 非交互式 Canvas(例如背景圖像)則移除 Graphic Raycaster,避免不必要的檢測。
- 關閉非交互元素的
Raycast Target
- 對於不需要觸控的 UI 元素(如純裝飾性圖片),關閉其 Raycast Target 設定。
- 在 Image 組件中,取消勾選 Raycast Target。
- 這樣可以防止該元素被包含在射線檢測的範圍內,減少計算負擔。
- 對於不需要觸控的 UI 元素(如純裝飾性圖片),關閉其 Raycast Target 設定。
- 使用阻擋遮罩(Blocking Mask)
- 當 Canvas 的 Render Mode 設置為 Worldspace Camera 或 Screen Space Camera 時,可以使用 Blocking Mask:
- 該遮罩決定 Raycaster 是否使用 2D 或 3D 物理來檢測阻擋物。
- 如果物理檢測對 UI 無直接影響,避免啟用該功能以節省資源。
- 當 Canvas 的 Render Mode 設置為 Worldspace Camera 或 Screen Space Camera 時,可以使用 Blocking Mask:
- 分割 Canvas
- 將靜態和動態 UI 元素分離至不同的 Canvas。
- 靜態 Canvas 不需要頻繁更新,也不需要 Graphic Raycaster。
例子:
假設遊戲的 UI 包含以下部分
- 主菜單:大部分按鈕需要觸控響應,因此保留 Graphic Raycaster,並確保按鈕的 Raycast Target 為啟用狀態。
- 背景圖片:純裝飾性元素,關閉 Raycast Target 並移除其 Canvas 的 Graphic Raycaster。
- 玩家 HUD(血量條、得分顯示):分配到單獨的 Canvas,僅需要檢測少數互動(例如按鈕點擊)。
管理 UI 對象池
問題:在常見的 UI 對象池使用方式中,開發者通常先更改對象的父節點(parent),再禁用對象(disable)。然而,這樣的操作會導致:
- 多次改變層級結構:反復更新對象的層級關係(hierarchy),使得整體性能受到影響。
- 不必要的開銷:每次改變父節點或激活/禁用對象時,Unity 會標記整個層級結構為“髒”,從而增加開銷。
解決方案:優化對象激活與重設順序
- 為提高效率,建議按照以下順序管理 UI 對象池:
- 禁用對象再更改父節點:
- 禁用對象(SetActive(false))後再將其重新分配到對象池的父節點。
- 這樣可以確保原始層級結構僅被標記一次為“髒”,避免重複更新。
- 禁用對象再更改父節點:
- 從對象池提取時的順序:
- 先更改父節點:將對象移動到新父節點中。
- 更新對象數據:重設對象的數據(如 UI 文本或圖片)。
- 最後激活對象:使用 SetActive(true) 啟用對象。
- 這樣的流程可將每個對象的層級結構變化降至最少,從而減少不必要的性能開銷。
隱藏 Canvas 的方法
在開發過程中,可能需要暫時隱藏某些 UI 元素或整個 Canvas。常見的方式有
使用 SetActive(false) 禁用整個 GameObject
- 可能會導致 Canvas 層級結構中的回調函數執行(如 OnDisable 和 OnEnable),增加不必要的性能開銷。
移動 Canvas 的位置,讓使用者看不見
- 並不能停止 GPU 的繪製操作,仍會影響性能。
改變透明度
,- 並不能停止 GPU 的繪製操作,仍會影響性能。
解決方案:停用 Canvas 組件,透過停用 Canvas 可以
- 停止繪製操作,當停用 Canvas 組件會立即停止向 GPU 發送繪製請求,使畫布變得不可見。
- 並保留頂點緩衝區,Canvas 的頂點資料(meshes 和 vertices)會被保留,因此在重新啟用時無需重建(rebulid),僅需恢復繪製操作。
- 避免不必要的回調,停用 Canvas 組件不會觸發整個層級結構的 OnDisable/OnEnable 回調,從而減少性能損耗。
UI 元素動畫
當在 UI 元素上應用 Animator 時,即使動畫的值保持不變,也會在每一幀對 UI 元素產生影響。這會導致
- 不必要的性能消耗,特別是在多個靜態 UI 元素上。
解決方案:針對靜態或偶爾改變的 UI 動畫需求,避免使用 Animator。可以透過以下方式實現高效的 UI 動畫。使用程式碼或 Tweening 系統進行動畫處理
- 手寫動畫程式碼:針對簡單的動畫需求,直接使用 C# 程式碼逐步更改 UI 屬性(如位置、透明度等)。
- Tweening 系統:使用輕量級的 Tweening 庫來簡化動畫實現。
- Tweening 系統通過插值逐步更改屬性,對於臨時或事件驅動的動畫需求非常高效。
- 使用專業的 Tweening 資產(如 DoTween)可以快速實現高效動畫。
開發 UI 建議
- 為最常用的 UI 元素建立 Prefabs
- 對於經常使用的 UI 元素(如標題文字),可以將其創建為 Prefab,並將所需的組件附加到這些 Prefab 上。這樣一來,當你需要修改某些元素時,所有使用該 Prefab 的地方都會自動更新,讓你更輕鬆地管理與修改 UI 元素。
- 例子:假設你有多個地方需要顯示標題文字,將標題文字做成 Prefab,並在遊戲中重複使用。未來如果需要調整字體或大小,只需更改 Prefab,就能同步更新所有相關元素。
- 使用 Sprite Atlas
- 在 Unity 中使用 Sprite Atlas 來將多個 Sprite 紋理打包到一個單一的紋理資源中。這對於優化遊戲效能非常有幫助,因為它減少了渲染過程中的 draw calls,從而提升遊戲性能,特別是在移動設備上。
- Sprite Atlas 的優點:
- Draw call 優化:使用 Sprite Atlas,可以減少 draw calls,讓 Unity 在渲染多個 Sprite 時只用一個 draw call,大大提高性能。
- 紋理分組:Sprite Atlas 讓你能夠將多個 Sprite 或紋理打包在一起,便於管理和組織資源。
- 自動打包:Unity 會自動進行紋理打包,將個別的 Sprite 儘可能地安排在 Atlas 中,減少空白區域的浪費,並優化紋理使用。
- Mipmapping 支援:Sprite Atlas 支援 Mipmapping,這能夠提高在遠距離查看紋理時的渲染質量。
- 平台適配:可以為不同的裝置平台或螢幕解析度創建不同的 Sprite Atlas 變體,保證在各種設備上的最佳效能。
- Unity 編輯器整合:你可以直接在 Unity 編輯器中創建與管理 Sprite Atlas,方便遊戲開發者進行資源的視覺化與調整。
- 使用透明圖片疊加進行設計對齊
- 將設計圖層疊加一個稍微透明的圖片,這樣可以幫助你更準確地對齊並組織 UI 視圖,使其符合最終設計。
MVVM
在 Unity 中實作 MVVM
1 | // Model |
1 | // ViewModel |
1 | // View |