UGUI 性能優化系列:第一篇——基礎優化與資源管理

UGUI 性能優化系列:第一篇——基礎優化與資源管理

UGUI 性能優化系列:第二篇——Canvas 與 UI 元素管理

在 Unity 游戲中,用戶界面(UI)是玩家與游戲交互的核心。然而,不當的 UGUI 使用常常成為游戲性能的瓶頸,尤其是在移動設備上。理解 UGUI 的工作原理并掌握其優化技巧,是每個 Unity 開發者必備的技能。本系列的第一篇文章,我們將從最基礎的層面出發,深入探討 UGUI 的渲染管線,以及如何從 資源管理 的角度進行優化,為后續更深入的優化打下堅實基礎。


一、UGUI 渲染管線簡介

要優化 UGUI,首先需要了解它是如何將 UI 元素繪制到屏幕上的。UGUI 的渲染過程可以簡化為以下幾個核心步驟:

1. UI 元素層級與事件系統

當我們在 Hierarchy 窗口中創建 UI 元素時,它們會形成一個父子層級結構。UGUI 會根據這個層級結構以及它們的 Rect Transform 屬性來計算每個 UI 元素的最終位置和大小。同時,UGUI 的事件系統 (EventSystem) 負責處理用戶的輸入(如點擊、拖拽),并將其分發給相應的 UI 元素。

2. Mesh 生成與更新

UGUI 中的每個可渲染的 UI 元素(如 Image, Text, RawImage 等)最終都需要被轉換為 Mesh(網格)才能被 GPU 渲染。這個 Mesh 包含了頂點的坐標、UV 坐標(用于紋理映射)和顏色信息。

  • 臟標記(Dirty Flag)與重建(Rebuild):
    當 UI 元素的某些屬性發生改變時(例如 Text 內容變化、ImageSprite 變化、Rect Transform 大小位置改變),UGUI 會給這個元素打上一個“臟標記”。在一個 Canvas 上,當任何子元素的“臟標記”被激活時,UGUI 會觸發該 Canvas 的 重建 過程。
    重建過程會重新計算受影響 UI 元素的 Mesh,并將新的 Mesh 數據提交給 GPU。這個過程是性能開銷的主要來源之一,因為 Mesh 的生成涉及到 CPU 計算,并且數據傳輸到 GPU 也會消耗帶寬。
3. 合批(Batching)

為了提高渲染效率,GPU 喜歡一次性接收大量數據進行處理,而不是零散地接收小批數據。因此,Unity 會嘗試將多個可以共用相同材質(Material)和紋理(Texture)的 UI 元素的 Mesh 合并成一個更大的 Mesh,然后一次性提交給 GPU 進行渲染。這個過程就是 合批(Batching)

  • Draw Call:
    每一次 GPU 接收到渲染指令(渲染一批 Mesh)并進行渲染的過程,被稱為一個 Draw Call。Draw Call 的數量是衡量渲染性能的關鍵指標之一。Draw Call 越多,CPU 和 GPU 之間的通信開銷就越大,性能也就越低。
    理想情況下,我們希望盡可能減少 Draw Call 的數量。UGUI 的合批機制正是為了達到這個目的。
4. 裁剪(Culling)與遮擋(Occlusion)
  • 裁剪: UGUI 會自動裁剪超出 Canvas 范圍的 UI 元素,這意味著那些完全在 Canvas 外部的 UI 元素不會被渲染。
  • 遮擋: 盡管 UGUI 并沒有像 3D 場景那樣的完整遮擋剔除機制,但它會根據 UI 元素的層級和 Rect Transform 的布局來確定哪些元素在 Z 軸上被其他元素完全遮擋,從而避免繪制那些完全被遮擋的像素。
5. 像素填充(Overdraw)

即使一個 UI 元素沒有被完全遮擋,它也可能與其他 UI 元素重疊。在重疊區域,GPU 可能需要多次繪制同一個像素,這個現象稱為 Overdraw(過度繪制)。Overdraw 發生在 GPU 層面,它會增加 GPU 的像素填充率(Fill Rate)壓力,尤其是在移動設備上,過高的 Overdraw 會顯著影響性能。

了解了這些基本原理,我們就可以有針對性地進行優化了。


二、Sprite Atlas(圖集)優化

圖集(Sprite Atlas) 是 UGUI 性能優化中最重要的手段之一。它的核心思想是將多個小圖片打包到一個大圖片中,從而減少 Draw Call 數量,提高渲染效率。

1. 為什么要使用圖集?

在 UGUI 中,每個 Image 組件通常引用一個 Sprite。如果每個 Sprite 都對應一張獨立的紋理圖片,那么在渲染時,每個 Image 都可能產生一個獨立的 Draw Call。想象一下一個復雜的 UI 界面,有幾十甚至上百個 Image 組件,這將導致 Draw Call 數量飆升,嚴重拖累性能。

使用圖集后,所有打包到同一個圖集中的 Sprite 共享一張大的紋理圖片。當這些 Sprite 被渲染時,只要它們使用相同的材質,Unity 就可以將它們的 Mesh 合并,一次性渲染,從而顯著減少 Draw Call。

優勢總結:

  • 減少 Draw Call: 這是最核心的優勢,直接降低 CPU 和 GPU 的通信開銷。
  • 減少內存占用(可能): 某些情況下,打包后的圖集可能比散圖的總和占用更少的內存,因為它避免了每個圖片單獨加載的額外開銷。
  • 提高加載速度: 加載一張大圖通常比加載多張小圖更快。
2. Unity 中的圖集制作與使用

Unity 提供了兩種主要的方式來創建和使用圖集:Sprite Packer手動圖集

a. Sprite Packer(推薦)

Sprite Packer 是 Unity 內置的自動圖集打包工具,它能自動檢測項目中符合條件的 Sprite 并將其打包成圖集。

使用步驟:

  1. 啟用 Sprite Packer:
    打開 Unity 編輯器,選擇 Edit > Project Settings > Editor。在 Sprite Packer 部分,將 Mode 設置為 EnabledEnabled For Builds

    • Enabled: 在編輯器和打包時都啟用 Sprite Packer。這會讓你在編輯器中也能看到打包后的效果,便于調試。
    • Enabled For Builds: 只在打包時啟用 Sprite Packer。在編輯器中會保持散圖狀態,性能消耗可能稍高,但對于一些不希望編輯器實時打包的場景可能更合適。通常建議設置為 Enabled
  2. 設置 Sprite 的 Packing Tag:
    選擇你想要打包的 Sprite 紋理(通常是 Texture Type 設置為 Sprite (2D and UI) 的圖片)。在 Inspector 窗口中,找到 Packing Tag 屬性。

    • 輸入一個相同的字符串作為 Packing Tag,所有擁有相同 Packing TagSprite 將會被打包到同一個圖集中。
    • 命名規范建議: Packing Tag 應該反映圖集的內容或用途,例如 UI_Common, UI_Icons, UI_Battle 等。
    • 你也可以在 Packing Tag 前面加上 [TIGHT][RECTANGLE] 來控制打包方式,但通常默認行為就足夠了。
  3. 打包圖集:

    • Sprite Packer 啟用后,Unity 會在特定時機(例如保存項目、進入 Play 模式、構建游戲)自動進行圖集打包。
    • 你也可以手動觸發打包:在菜單欄選擇 Window > 2D > Sprite Packer,然后點擊 Pack 按鈕。
    • Sprite Packer 窗口中,你可以預覽打包后的圖集,以及各個 Sprite 在圖集中的位置。
  4. 在 UI 中使用:

    • 一旦 Sprite 被打包成圖集,你在 Image 組件中引用這些 Sprite 時,它們會自動使用打包后的圖集。你無需做額外的修改。

Sprite Packer 的優缺點:

  • 優點:
    • 自動化: 大大簡化了圖集管理工作,無需手動調整布局。
    • 高效: Unity 內部算法會自動優化圖集尺寸和排布,最大化空間利用率。
    • 易于維護: 添加、刪除或修改 Sprite 后,Unity 會自動重新打包。
  • 缺點:
    • 不可控性: 開發者對最終圖集的大小和布局控制較少,可能不符合特定需求(例如,要求某個圖集固定尺寸)。
    • 構建時間: 項目中 Sprite 數量眾多時,每次打包都可能增加構建時間。
b. 手動圖集(舊版本或特定需求)

在較老的 Unity 版本或某些需要精確控制圖集內容的特殊情況下,開發者會選擇手動制作圖集。

使用步驟:

  1. 創建大圖: 使用 Photoshop、GIMP 等圖像編輯軟件,將多個小圖片拼接成一張大圖。
  2. 切割 Sprite: 將這張大圖導入 Unity,將其 Texture Type 設置為 Sprite (2D and UI)。在 Inspector 窗口中,將 Sprite Mode 設置為 Multiple。然后點擊 Sprite Editor 按鈕,手動或自動(通過 Slice 功能)將大圖切割成多個 Sprite
  3. 在 UI 中使用:Image 組件中,直接引用這些手動切割出來的 Sprite

手動圖集的優缺點:

  • 優點:
    • 完全可控: 開發者可以精確控制圖集的內容、尺寸和布局。
    • 適用于復雜場景: 當需要將一些并非 Sprite 類型(如 RawImage 或 3D 模型的貼圖)的圖片手動打包到一起時,這是一種靈活的方式。
  • 缺點:
    • 繁瑣: 手動拼接和切割工作量大,尤其是在項目后期需要頻繁修改時。
    • 低效: 人工排布通常不如自動算法高效,可能造成空間浪費。
    • 維護困難: 增刪改 Sprite 需要重新編輯大圖并重新切割,容易出錯。

結論: 除非有非常特殊的理由,否則強烈建議使用 Sprite Packer 來管理圖集。

3. 圖集大小、格式與壓縮

圖集的尺寸和格式直接影響內存占用和加載速度。

a. 圖集尺寸
  • 推薦尺寸: 大多數情況下,圖集尺寸應為 2 的冪次方(例如 256x256, 512x512, 1024x1024, 2048x2048, 4096x4096)。這是因為 GPU 在處理 2 的冪次方的紋理時效率最高。
  • 最大尺寸: 檢查目標平臺的 GPU 支持的最大紋理尺寸。通常移動設備支持 2048x2048 或 4096x4096。超過這個尺寸的紋理可能無法加載或被迫降采樣,反而浪費資源。
  • 合理控制: 避免創建過大或過小的圖集。過大的圖集會增加內存占用和加載時間,過小的圖集可能導致無法有效合批。
b. 圖片格式與壓縮

圖集的格式和壓縮方式直接決定了其在內存中的大小和渲染性能。

  • RGBA 32-bit: 默認格式,每個像素 32 位(R, G, B, A 各 8 位),提供最高的圖像質量,但內存占用最大。適用于需要高質量透明度的 UI 元素。
  • RGBA 16-bit: 每個像素 16 位,質量略有下降但內存占用減半。在對畫質要求不是特別高的情況下,可以考慮使用。
  • RGB 24-bit: 沒有透明度通道,內存占用低于 RGBA。適用于不透明的 UI 元素,但 UGUI 中帶透明度的 UI 元素占多數。
  • ETC2 (Android & OpenGL ES 3.0+):
    • ETC2 RGB4: 無透明度,適用于不帶 Alpha 的圖片,壓縮比高。
    • ETC2 RGB4 A1: 1 位 Alpha 通道,適用于只有完全透明或完全不透明的圖片。
    • ETC2 RGBA8: 8 位 Alpha 通道,支持高質量透明度,壓縮比適中。
    • 優勢: 硬件解壓,GPU 直接讀取,無需 CPU 解壓,效率高,內存占用小。
    • 缺點: 僅支持 Android 和部分 iOS 設備(OpenGL ES 3.0+),兼容性不如 ASTC。
  • ASTC (Android & iOS):
    • 自適應可伸縮紋理壓縮: 更先進的紋理壓縮格式,提供更高的壓縮比和更好的圖像質量。
    • 塊大小: 可以選擇不同的塊大小(如 4x4, 6x6, 8x8, 12x12),塊越小質量越好,內存越大;塊越大質量越差,內存越小。
    • 優勢: 兼容性更好(Android 和 iOS 廣泛支持),壓縮質量和效率通常優于 ETC2。
    • 缺點: 壓縮時間可能較長,對硬件支持有一定要求。
  • PVRTC (iOS Only):
    • PowerVR 紋理壓縮: 針對 PowerVR GPU 優化的壓縮格式(早期 iOS 設備主要使用),有 2-bit 和 4-bit 兩種。
    • 優勢: 在特定 iOS 設備上表現優秀。
    • 缺點: 質量相對較差,且僅限 iOS 平臺。

選擇建議:

  1. 優先考慮平臺專屬壓縮格式:
    • Android: 優先使用 ASTC,其次是 ETC2
    • iOS: 優先使用 ASTC,其次是 PVRTC(如果目標設備較舊且對內存極致敏感)。
    • PC/Standalone: 通常可以使用 DXT1 (RGB)DXT5 (RGBA)
  2. 根據圖片內容選擇:
    • 對于不需要透明度的圖片:選擇 RGB 格式或對應平臺的無 Alpha 壓縮格式(如 ETC2 RGB4, ASTC NxB 無 Alpha 塊)。
    • 對于需要透明度的圖片:選擇 RGBA 格式或對應平臺的帶 Alpha 壓縮格式(如 ETC2 RGBA8, ASTC NxB 帶 Alpha 塊)。
  3. 權衡質量與內存: 在保證視覺效果的前提下,盡量選擇壓縮比最高的格式。

在 Unity 中,你可以通過選擇紋理圖片,在 Inspector 窗口中設置 Texture TypeSprite (2D and UI),然后在 Platform Specific Overrides 中針對不同平臺設置不同的壓縮格式。

4. 動態圖集與靜態圖集

盡管 Sprite Packer 是自動打包,但我們仍然可以從邏輯上區分動態圖集和靜態圖集。

  • 靜態圖集:
    • 定義: 指那些在游戲運行過程中內容不會改變,或者變化非常少的圖集。例如,主界面的通用圖標、按鈕背景、HUD 元素等。
    • 優勢: 一次加載,永久使用,內存開銷穩定。
    • 管理: 將所有相關的靜態 Sprite 都放在一個或幾個大的圖集中,通過 Packing Tag 來區分。
  • 動態圖集:
    • 定義: 指那些內容會根據游戲進度、玩家選擇等動態加載和卸載的圖集。例如,某個特定副本的怪物頭像、裝備圖標、特定任務的 UI 元素等。
    • 優勢: 按需加載,減少初次加載時間,節省內存。
    • 管理: 為不同模塊、不同場景的動態 UI 元素創建獨立的圖集。當某個模塊不再使用時,可以卸載對應的圖集資源。
    • 注意事項: 頻繁加載和卸載圖集本身也會有性能開銷,需要權衡。可以考慮使用 AssetBundleAddressables 來管理動態圖集的加載和卸載。

規劃建議:

  • 將所有在游戲中頻繁出現、通用性強的 UI 元素(如通用按鈕、通用圖標、背景、通用字體)打包到一個或幾個大的 “公共圖集” 中。
  • 針對特定模塊或場景(如戰斗界面、背包界面、商店界面),將只在該模塊或場景中使用的 UI 元素打包成獨立的 “模塊圖集”
  • 避免將無關的 Sprite 打包到同一個圖集中,這可能導致圖集過大或無法有效卸載。

三、字體優化

字體在 UGUI 中也扮演著重要角色,其渲染方式也會影響性能。

1. 字體的渲染原理

當你在 UGUI 中使用 TextTextMeshPro 組件時,字體字符實際上也是以紋理和 Mesh 的形式被渲染的。

  • 字體紋理(Font Atlas): 每個字符都會被渲染到一張紋理上,這張紋理就是字體圖集(Font Atlas)。當需要顯示某個字符時,UGUI 會從這張字體圖集中獲取對應字符的 UV 信息,并將其繪制到屏幕上。
  • Mesh 生成: 每個字符都會被轉換成四邊形 Mesh,這些 Mesh 包含了字符的形狀信息。當文本內容發生變化時,對應的 Mesh 需要重新生成。
2. 動態字體與靜態字體(TextMeshPro 的優勢)
a. 動態字體(Dynamic Font)
  • 原理: Unity 默認的 Text 組件通常使用動態字體。當你導入一個 .ttf.otf 字體文件時,Unity 會在運行時根據需要動態生成字符紋理和 Mesh。這意味著只有當某個字符被用到時,它才會被加入到字體圖集中。
  • 優勢: 初始包體較小,因為不需要預生成所有字符紋理。
  • 缺點:
    • 運行時開銷: 第一次使用某個字符時,需要實時渲染并生成其紋理,這會產生一定的 CPU 開銷。如果文本內容頻繁變化且包含大量新字符,這種開銷會累積。
    • 字體圖集擴展: 隨著使用的字符越來越多,字體圖集會不斷擴展,如果擴展次數過多,可能導致 Draw Call 增加或內存碎片。
    • 渲染質量: 默認 Text 組件的渲染質量通常不如 TextMeshPro
b. 靜態字體(Pre-generated Font Atlas / SDF Font)
  • 原理: TextMeshPro(簡稱 TMP)是 Unity 推薦的文本解決方案,它采用 SDF(Signed Distance Field,有符號距離場) 技術。在使用 TMP 時,我們通常會預先生成一個包含所有常用字符的字體圖集(Font Atlas)。
  • 優勢:
    • 高質量渲染: SDF 字體在放大或縮小時依然保持清晰,沒有鋸齒感,渲染效果遠優于傳統動態字體。
    • 性能穩定: 字體圖集在游戲啟動時一次性加載,運行時無需動態生成字符紋理,避免了額外的 CPU 開銷和字體圖集擴展問題。
    • 更少的 Draw Call: TMP 會嘗試將所有使用相同字體和材質的文本合并成一個 Draw Call。
    • 豐富的文本效果: TMP 內置了描邊、陰影、漸變等多種文本效果,且性能開銷小。
  • 缺點:
    • 包體增大: 預生成的字體圖集會增加游戲包體大小。
    • 初次加載: 字體圖集越大,初次加載時間越長。

建議: 強烈推薦使用 TextMeshPro 來處理所有文本顯示。它的優點遠遠超過缺點。對于一些極端需要控制包體大小的場景,可以考慮只打包常用的字符集。

3. 字體 Atlas 的生成與管理 (TextMeshPro)

當你使用 TextMeshPro 時,字體 Atlas 的管理變得尤為重要。

a. 生成字體 Atlas:
  1. 導入字體: 將你的字體文件(.ttf 或 .otf)導入 Unity 項目。
  2. 創建字體 Asset: 選中字體文件,右鍵 Create > TextMeshPro > Font Asset
  3. 配置字體 Asset:
    • 在生成的 Font Asset 文件上,點擊 Open Font Asset Creator 按鈕。
    • Source Font: 你的字體文件。
    • Font Size: 用于生成字體圖集時采樣的字體大小,越大生成的圖集質量越高,但圖集占用空間越大。通常 90-128 足夠。
    • Padding: 字符之間的填充距離,用于防止字符邊緣鋸齒和裁剪。
    • Atlas Resolution: 字體圖集的尺寸,通常選擇 2048x2048 或 4096x4096。
    • Character Set: 選擇要包含的字符集。
      • ASCII: 僅包含基本英文字符。
      • Extended ASCII: 包含更多歐洲語言字符。
      • Unicode Hex Range: 自定義 Unicode 范圍,適用于特定語言字符。
      • Custom Characters: 手動輸入字符。
      • Characters From File: 從文本文件加載字符列表。
      • 最常用的是 Characters From File 準備一個包含游戲中所有可能用到的中文字符的文本文件,然后導入。這能最大程度地壓縮字體圖集大小,同時保證所有字符可用。
    • Render Mode: 通常選擇 Distance Field (SDF) 以獲得最佳效果。
    • 生成: 點擊 Generate Font Atlas 按鈕,然后保存生成的 Font Asset。
b. 字體 Atlas 的管理:
  • 字符集管理: 最重要的優化是控制字體 Atlas 中的字符數量。只包含游戲中實際會用到的字符,而不是全部字符。
    • 對于中文游戲,需要收集所有文本內容,提取出唯一的字符,然后生成一個字符列表文件。
    • 對于多語言游戲,為每種語言或語言組生成獨立的 Font Asset,按需加載。
  • 復用 Font Asset: 確保所有使用相同字體的 TextMeshPro 組件都引用同一個 Font Asset,這樣才能最大程度地實現合批。
  • 優化圖集尺寸: 在保證清晰度的情況下,選擇最小的 Atlas Resolution
  • 多個 Font Asset: 如果游戲中有多種風格差異很大的字體,或者某種字體只在特定場景使用,可以創建多個 Font Asset,并按需加載。例如,標題字體一個 Asset,正文字體一個 Asset。

四、圖片資源優化

除了圖集,單個圖片資源的優化也至關重要,它們是構建 UI 的基本塊。

1. 圖片格式與壓縮

這部分與圖集優化中的圖片格式和壓縮原理相同,但針對的是那些不適合打包成圖集或作為 RawImage 使用的獨立圖片。

  • 紋理類型 (Texture Type):
    • Sprite (2D and UI): 用于 UI Image 組件中的 Sprite
    • Texture: 用于 RawImage 組件或 3D 模型的紋理。
  • Read/Write Enabled:
    • 默認情況下,Read/Write Enabled 是關閉的。這意味著 CPU 無法直接訪問紋理數據,從而節省內存。
    • 除非你需要在運行時通過腳本讀寫紋理像素(例如生成截圖、進行像素級操作),否則務必保持 Read/Write Enabled 為關閉狀態。 開啟它會使紋理在內存中保留一份 CPU 可讀副本,導致內存占用翻倍。
  • Generate Mip Maps:
    • Mip Maps 是紋理的不同分辨率副本,用于在物體距離攝像機較遠時使用低分辨率的紋理,從而提高渲染效率和消除摩爾紋。
    • 對于 UGUI 紋理,通常不需要 Mip Maps UI 元素通常是 2D 的,且不會因為距離變化而顯著縮小。開啟 Mip Maps 會增加 33% 的內存占用。因此,在 Sprite 和 UI 紋理的 Import Settings 中,請 禁用 Generate Mip Maps
  • Filter Mode:
    • Point (No Filter):最近鄰采樣,像素化效果,用于像素藝術。
    • Bilinear:雙線性過濾,平滑過渡,用于大多數 UI。
    • Trilinear:三線性過濾,在 Mip Maps 之間平滑過渡,但 UI 不開 Mip Maps,所以選擇 Bilinear 即可。
2. 圖片尺寸與分辨率的合理設置

圖片尺寸是影響內存占用和渲染性能的另一個關鍵因素。

  • 最小化尺寸: 圖片尺寸應該 剛好滿足 UI 元素在屏幕上顯示的最高分辨率要求。不要使用過大的圖片,然后讓 Unity 縮放。例如,如果一個按鈕圖標在 UI 中最大顯示為 64x64 像素,那么其原始圖片尺寸就應該是 64x64,而不是 256x256。
    • 計算方式: 考慮 UI 在不同分辨率設備上的縮放。如果你使用的是 Canvas ScalerScale With Screen Size 模式,你需要根據你設置的 Reference Resolution 和目標分辨率來計算實際渲染尺寸。
  • 避免非 2 的冪次方: 盡管現代 GPU 對非 2 的冪次方紋理支持良好,但對于一些舊設備或特定壓縮格式,使用 2 的冪次方尺寸(如 128x128, 256x256)仍然是更安全的做法,并且可能在內部處理上更高效。
  • 統一分辨率: 盡量在美術資源導出時就統一好圖片的分辨率。例如,如果你的基準分辨率是 1920x1080,那么所有 UI 元素都應該根據這個分辨率來設計其最佳顯示尺寸。
  • LOD(Level of Detail)for UI? 盡管 Unity 有 LOD 系統,但它主要用于 3D 模型。對于 UGUI,通過控制圖片尺寸和圖集來達到類似的目的更為實際。例如,對于需要放大的 UI,提供更高分辨率的圖集;對于縮小或背景元素,可以使用較低分辨率的圖集。
3. 避免使用未經優化的圖片資源
  • 美術規范: 與美術團隊建立良好的溝通,讓他們了解性能優化的要求。
    • 導出格式: 優先導出 PNG(帶 Alpha)或 JPG(無 Alpha)。
    • 裁剪透明像素: 確保圖片邊緣沒有多余的透明像素,這會增加不必要的內存占用和 Draw Call。在 Photoshop 中使用 TrimCrop 功能。
    • 統一尺寸: 如果是系列圖標或按鈕,盡量保持其導出尺寸統一,便于圖集打包。
  • 檢查圖片冗余: 項目中是否存在多余的、未使用的圖片資源?使用 Unity 的 Editor 擴展或插件來檢測并刪除它們。
  • 利用 .psd 導入: Unity 可以直接導入 .psd 文件,并將其切割為 Sprite。這對于美術迭代非常方便,但要確保最終導出到游戲中的圖片是經過優化的。在導入 .psd 文件后,通常需要調整其 Import Settings 以應用合適的壓縮。

五、Batching(合批)原理與優化

合批是 UGUI 渲染優化的核心,直接影響 Draw Call 數量。

1. 合批條件

Unity 的 UGUI 合批機制主要依賴于以下幾個條件:

  • 相同 Canvas: 只有在同一個 Canvas 下的 UI 元素才可能進行合批。
  • 相同 Material: 這是最核心的條件。所有參與合批的 UI 元素必須使用 完全相同的 Material 實例
    • 如果 UI 元素的 Material 屬性不同,或者即使材質文件相同但參數被修改導致生成了不同的材質實例,都無法合批。
    • Image 組件的 Color 屬性通常不會破壞合批,因為顏色是通過頂點顏色傳遞給 Shader 的。
  • 相同 Texture: 如果 Material 中引用了紋理,那么這些紋理也必須是相同的。
    • 這就是為什么圖集如此重要的原因:圖集中的所有 Sprite 都共享同一個大紋理,從而滿足這個條件。
  • 渲染順序: UI 元素的渲染順序也至關重要。如果兩個可以合批的元素之間插入了一個無法合批的元素,那么合批就會被中斷。
    • Z 軸順序: UGUI 的渲染是基于 Z 軸(Order in Layer, Rect Transform 的 Z 坐標)和 Hierarchy 中的順序。越靠后的 UI 元素越靠前渲染。
    • 透明與不透明: 透明元素和不透明元素的渲染批次是分開的。通常不透明元素先渲染,透明元素后渲染。將透明度高的 UI 元素(如半透明背景)放在不透明元素之后,可以提高合批效率。
2. 合批的種類
a. Dynamic Batching(動態合批)
  • 原理: Unity 會在 CPU 上將滿足合批條件的小型 Mesh 合并成一個更大的 Mesh,然后一次性提交給 GPU。
  • UGLI 中的表現: UGUI 的合批機制就是 Dynamic Batching 的一種特殊形式。
  • 限制:
    • 合并的頂點數量限制(通常為 300-900 左右,具體取決于 Unity 版本和平臺)。如果合并后的 Mesh 頂點數量超過這個限制,就會分成多個批次。
    • Mesh 屬性:如果 Mesh 的法線、切線、UV0 以外的 UV 通道、頂點顏色等屬性不同,也可能無法合批。但 UGUI 的 Mesh 通常比較簡單,很少會遇到這些限制。
b. Static Batching(靜態合批)
  • 原理: 在構建游戲時,將標記為靜態的對象合并成一個或幾個大 Mesh。
  • UGUI 中適用性: 不適用于 UGUI。 UGUI 元素通常是動態的(需要響應交互、動畫等),不適合標記為 Static。將 UI 元素標記為 Static 可能導致意外行為或無法進行合批。
3. 如何通過合理組織 UI 元素來促進合批

減少 Draw Call 的關鍵在于盡可能讓更多的 UI 元素滿足合批條件。

  • 統一 Material:

    • 確保所有需要合批的 UI 元素使用相同的 Material。默認的 UGUI ImageText 組件都使用 UI/Default Shader 和 Material。
    • 如果你自定義了 UI Shader,確保使用該 Shader 的所有 UI 元素都使用同一個 Material 實例。
  • 使用 Sprite Atlas: 這是最關鍵的一步,保證所有 Image 組件引用來自同一個圖集的 Sprite

  • 調整 UI 層級與渲染順序:

    • 將能合批的元素放在一起: 在 Hierarchy 窗口中,將那些可以合批的 UI 元素(例如,同一張圖集的不同圖標)盡量放在同一個 Canvas 下,并且在層級上盡可能靠近。
    • 避免交叉: 如果 A、B、C 三個 UI 元素,A 和 C 可以合批,B 無法合批。如果層級是 A -> B -> C,那么 A 和 C 就無法合批,會產生兩個 Draw Call。理想的層級應該是 A -> C -> B,這樣 A 和 C 就可以合批,只產生一個 Draw Call。
    • 透明度:
      • 避免半透明與不透明 UI 元素交錯: 通常,不透明的 UI 元素先渲染,半透明的 UI 元素后渲染。如果它們交錯排列,會導致 Draw Call 頻繁切換,從而打斷合批。
      • 最佳實踐: 將所有不透明的 UI 元素放在一個層級或 Canvas 下,然后將所有半透明的 UI 元素放在另一個層級或 Canvas 下。
  • Canvas 的切割與分層(下篇會詳細講解):

    • 將一個大 Canvas 切割成多個小 Canvas,可以更精細地控制 UI 元素的重建范圍。
    • 同時,分層后的 Canvas 也更容易進行 Draw Call 的優化,因為每個 Canvas 都可以獨立地進行合批。
  • 減少 Mask 組件的使用:

    • Mask 組件(包括 Rect Mask 2D)會打斷合批。因為 Mask 會修改渲染狀態(裁剪范圍),導致其內部和外部的元素無法合批。
    • 盡量減少 Mask 的使用,或者只在必要的地方使用。對于簡單的裁剪需求,可以考慮使用 ImageTypeFilledSliced 來實現。
  • 善用 Unity Profiler 和 Frame Debugger:

    • Profiler: 在 Profiler 的 CPU UsageGPU Usage 模塊中,你可以看到 UI.Render 的開銷,以及 Draw Call 的數量。
    • Frame Debugger: 這是分析 Draw Call 和合批情況的利器。
      • 打開 Window > Analysis > Frame Debugger
      • 在 Frame Debugger 中,你可以一步步查看每個 Draw Call 渲染了哪些對象,以及 Draw Call 為什么被中斷(例如 Material changed, Shader changed, Texture changed 等)。通過分析 Frame Debugger,你可以準確找出導致 Draw Call 增加的原因,并有針對性地進行優化。

六、總結與展望

本篇文章我們深入探討了 UGUI 渲染的基礎原理,并詳細講解了如何從 資源管理 的角度進行優化,包括:

  • 理解 UGUI 的 渲染管線Draw Call 的概念。
  • 通過 Sprite Atlas (圖集) 大幅減少 Draw Call,并學會選擇合適的打包方式(推薦 Sprite Packer)和紋理壓縮格式。
  • 強調 TextMeshPro 在字體渲染上的巨大優勢,以及如何優化其 字體 Atlas
  • 學會優化 圖片資源 的尺寸、格式和導入設置,避免不必要的內存開銷。
  • 深入理解 合批(Batching) 的條件,并掌握通過合理組織 UI 元素來促進合批的方法。
  • 學會使用 Unity ProfilerFrame Debugger 來分析和定位 Draw Call 問題。

這些基礎知識和優化策略是 UGUI 性能優化的基石。掌握它們,你就能有效地減少游戲在 UI 渲染上的性能開銷。

在下一篇文章中,我們將進一步深入,聚焦于 Canvas 的重建機制,以及如何通過 Canvas 分層UI 元素管理 來實現更高級別的性能優化。

UGUI 性能優化系列:第一篇——基礎優化與資源管理

UGUI 性能優化系列:第二篇——Canvas 與 UI 元素管理

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/89475.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/89475.shtml
英文地址,請注明出處:http://en.pswp.cn/web/89475.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

多端協同的招聘系統源碼開發指南:小程序+APP一體化設計

當下,很多企業選擇搭建屬于自己的多端協同招聘平臺,尤其是中大型人力資源公司、連鎖品牌企業,以及同城服務平臺,更是將“小程序APP”一體化招聘系統視為提升效率、降低用工成本的利器。 今天,筆者將從源碼開發的角度&a…

Maven 配置文件核心配置:本地倉庫、鏡像與 JDK 版本

Maven 配置文件核心配置:本地倉庫、鏡像與 JDK 版本 在 Maven 項目開發中,合理配置 settings.xml 文件能顯著提升依賴管理效率。本文將聚焦本地倉庫、鏡像加速和 JDK 版本這三個核心配置,結合 IDEA 環境詳細講解配置方法與作用。 一、Maven 配…

【時時三省】(C語言基礎)通過指針引用字符串

山不在高,有仙則名。水不在深,有龍則靈。 ----CSDN 時時三省如在printf函數中輸出一個字符串。這些字符串都是以直接形式(字面形式)給出的,在一對雙撇號中包含若干個合法的字符。使用字符串的更加靈活方便的方法——通…

【Linux驅動-快速回顧】一文快速理解GIC內部寄存器對中斷的控制

第一部分:GIC的功能和組成 1. GIC要解決的根本問題 在一個復雜的片上系統(SoC)中,有非常多的硬件模塊(如定時器、串口、按鍵、DMA等),它們都需要在完成任務或遇到特定事件時通知CPU。同時&#…

【IoTDB 線上小課 17】開源 ≠ 免費,3 分鐘總結開源商用指南

【IoTDB 視頻小課】第十七期,解答你最關心的開源商業使用問題!關于 IoTDB,關于物聯網,關于時序數據庫,關于開源...一個問題重點,3-5 分鐘,我們講給你聽:原來開源商業化有這么多規則開…

VUE項目學習筆記 v-for綁定數據,該數據異步獲取,同時需要對v-for的DOM節點進行js操作

問題描述:項目里有一個輪播圖,輪播圖的圖片數據從服務器獲取,用v-for生成DOM在頁面中顯示,輪播圖插件會通過new Swiper給DOM添加CSS、事件等,實現輪播效果。在這里存在操作順序問題:當服務器返回圖片數據后…

Science | 如何利用“T細胞+組蛋白乙酰化”兩大國自然熱點?T細胞耗竭機制與代謝-表觀遺傳調控的新范式

CD8?T細胞耗竭(T cell exhaustion)是腫瘤免疫治療的核心瓶頸,其表觀遺傳重塑機制(如組蛋白修飾)是當前國自然重點資助的前沿方向。耗竭T細胞(TEX)是指在慢性感染(如持續性病毒感染&…

Logback 配置的利器:深入理解<property>與<variable>

在構建現代 Java 應用程序時,日志是不可或缺的一部分。一個健壯的日志系統不僅能幫助我們監控應用程序的運行狀態,還能在問題發生時提供關鍵的診斷信息。Logback 作為 SLF4J 的一個流行實現,以其高性能和靈活的配置而廣受開發者喜愛。 然而&a…

Java中excel字典轉換

? 背景說明EasyExcel 原生的 ExcelProperty 注解不支持 dictType(不像那樣有 Excel(dictType"xxx")),所以如果你想實現字典翻譯功能,就需要自己實現 Converter 接口,比如 DictConvert。? 什么是 DictConve…

數據結構-3(雙向鏈表、循環鏈表、棧、隊列)

一、思維導圖二、雙向循環鏈表的判空、尾插、遍歷(反向)、尾刪class Node:def __init__(self, data):self.data dataself.next Noneself.prior Noneclass circularDoublyLinkedList():def __init__(self):self.head Noneself.tail Noneself.size 0def isEmpty(self):retu…

IDEA運行Tomcat一直提示端口被占用(也查不到該端口)

首先查看是否是因為Java程序異常終止,進程沒有完全釋放導致。打開資源管理器,找到所有的java.exe,強制結束任務。如果仍然不行,那就極可能還是開啟了hyper-V虛擬化,查看排除的tcp端口范圍端口號沒被占用卻提示占用&…

AWS Lambda 最佳實踐:構建高效無服務器應用的完整指南

引言 AWS Lambda 作為無服務器計算的核心服務,讓開發者能夠專注于業務邏輯而無需管理服務器。本文將通過實際案例和代碼示例,分享 Lambda 開發中的關鍵最佳實踐。 1. 函數設計原則 單一職責原則 每個 Lambda 函數應該只做一件事,這樣更容易測試、維護和擴展。 # ? 不推…

29、鴻蒙Harmony Next開發:深淺色適配和應用主題換膚

目錄 應用深淺色適配 應用跟隨系統的深淺色模式 應用主動設置深淺色模式 系統默認判斷規則 使用建議與限制 設置應用內主題換膚 概述 自定義主題色 設置應用內組件自定義主題色 設置應用局部頁面自定義主題風格 設置應用頁面局部深淺色 系統缺省token色值 應用深淺…

源鑒SCA4.9︱多模態SCA引擎重磅升級,開源風險深度治理能力再次進階

SCA技術已成為數字供應鏈開源治理的關鍵入口。源鑒SCA深度融合懸鏡原創專利級AI智能代碼疫苗技術,是國內首款集源碼組件成分分析、代碼成分溯源分析、制品成分二進制分析、容器鏡像成分掃描、運行時成分動態追蹤及開源供應鏈安全情報預警分析等六大核心引擎為一體的…

Git語義化提交規范及提交模板設置

Git語義化提交規范(Conventional Commits) 📚 常見的語義化提交類型包括:類型含義說明feat? 新增功能(feature)fix🐛 修復 bugdocs📚 修改文檔(如 README)st…

用TensorFlow進行邏輯回歸(五)

Softmax分類#List3-50%matplotlib inlineimport numpy as npimport tensorflow as tfimport matplotlib.pyplot as pltx1_label0 np.random.normal(1, 1, (100, 1))x2_label0 np.random.normal(1, 1, (100, 1))x1_label1 np.random.normal(5, 1, (100, 1))x2_label1 np.ran…

基于 Django + 協同過濾算法的電影推薦系統設計與實現

🎬 基于 Django 協同過濾算法的電影推薦系統設計與實現?? 本項目由成都理工大學宜賓校區的三位同學曾銘楊、楊皓麟、陳禧錦共同完成。項目以豆瓣電影數據為基礎,通過協同過濾算法為用戶構建個性化電影推薦服務,是一款集數據爬取、推薦算法…

小白全棧項目部署指南

小白全棧項目部署指南:前端后端數據庫完整攻略 📖 寫在前面 當你學會了基礎的靜態網站部署后,是不是想挑戰更有趣的項目?比如一個能夠注冊登錄、保存數據的完整應用? 這就需要學習全棧項目部署了! 別被&quo…

C# Linq 左關聯查詢詳解與實踐

在 C# 開發中,Linq(Language Integrated Query)提供了強大的數據查詢能力,尤其是在處理集合間的關聯操作時。本文將詳細解析 C# Linq 中的左關聯查詢,并通過實際案例說明其用法。左關聯查詢基礎左關聯(Left…

【機器學習深度學習】LoRA 微調詳解:大模型時代的高效適配利器

目錄 前言 一、LoRA 的核心思想 二、LoRA 為什么高效? ? 1. 參數效率 ? 2. 內存友好 ? 3. 即插即用 三、LoRA 適用場景 四、LoRA 實踐建議 五、LoRA 和全參數微調對比 六、 LoRA的具體定位 📌 總結 🔗 延伸閱讀 前言 在大模型…