Unity官方博文翻譯——UGUI優化05

首先附上原文地址:https://unity3d.com/cn/learn/tutorials/topics/best-practices/optimizing-ui-controls

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?優化UI控件

? ? ? ? UGUI優化指南的這一部分關注于一些特殊類型UI的問題。雖然說大多數UI控件在性能方面都比較類似,但有兩個組件會使游戲在接近發布狀態時造成許多性能問題。


UI Text

? ? ? ? Unity內置的Text組件是在UI內顯示柵格化字形的一種很便捷的方式。然而有一些并不被廣泛了解的特性,作為性能熱點高頻率的出現。每當給UI增加text的時候,要知道字形事實上是作為獨立的四邊形渲染的,每個字符一個。這些四邊形周圍通常有大量的多余閑置空間,這取決于它們的形狀,text這種位置的特點造成了一不小心就會打斷其他UI元素的batch。


Text的網格rebuild

? ? ? ? ?一個主要的問題是重建UI text網格,每當text組件被改變時,文本組件都必須重新計算多邊形來顯示實際的text。這個重新計算也發生在這個text組件或是它的父游戲物體被禁用或者啟用時,雖然這個text沒有改變。

? ? ? ? 這個行為對于任何要顯示大量文本標簽的UI都是一個問題,比如說大部分的排行榜或是統計界面。由于隱藏和顯示UGUI最簡單的方式是啟用和禁用包含UI的游戲物體,當擁有大量text組件的UI顯示時將經常會出現不期望的幀率卡頓。

? ? ? ? 有關這個問題的潛在解決方案,請看下一章的Disabling Canvas Renderers部分。(鏈接見原文)


動態字體和字體圖集

? ? ? ? 當要顯示的字符集合非常大或者在運行前并不知道有多少時,動態字體是一個非常方便的方式來顯示文本。Unity實現時,這些字體基于UI Text組件中遇到的字符,在運行是構建一個字符圖集。

? ? ? ? 加載每個字體對象將會保持自己紋理圖集,即使它與另一個字體在相同的字體體系中。例如,使用Arial字體的加粗的文本在一個控件上,同時使用Arial Bold字體在另外一個控件上將會產生一樣的顯示輸出。但是Unity將會保存兩份獨立的紋理圖集,一個是Arial字體另一個是Arial Bold字體。

? ? ? ? ?從一個優化的角度來講,要理解的最重要的事情是UGUI的動態字體圖集保存了每個單獨的由尺寸、風格、字符排列組合而成的字形。也就是說,如果一個UI包含兩個text組件,都顯示字母“A”,則:

? ? ? ? 1.如果兩個Text組件擁有相同的大小,則字體圖集中將包含一個字形。

? ? ? ? ?2.如果兩個text組件不擁有相同的大小(例如,一個是16號,另一個是24號),則字體圖集將包含兩個不同大小的字母“A”的副本。

? ? ? ? ?3.如果一個text組件是粗體而另一個不是,則字體圖集將同時包含粗體的“A”和常規的“A”。

? ? ? ? 無論何時一個擁有動態字體的UI text對象遇到了一個沒有被柵格化到字體紋理圖集的字形時,字體紋理圖集就必須被重建。如果新的字形可以適配進當前的圖集,它將被添加進去,并且圖形設備會重新加載這個圖集。然而,如果目前的圖集太小了,系統將會嘗試重建圖集,分為兩階段。

? ? ? ? ?首先,圖集會以相同的大小重建,只會用到當前激活的UI text組件(包括其父級Canvas啟用,但是禁用了Canvas Renderer組件的的UI Text組件)中要顯示的字形,如果系統成功的將所有現在要用到的字形適配進新圖集中,圖集的柵格化不會繼續進入第二步。

? ? ? ? 其次,如果這些現在使用到的字形不行被適配進當前大小的圖集,一個更大的圖集將會被創建出來,將這個圖集較短的尺寸放大二倍。比如,512x512的圖集將會被擴大到512x1024。

? ? ? ? 由于上述算法,一個動態字體圖集只會在被創建后增長尺寸。考慮到重建紋理圖集的性能消耗,使重建最小化是非常必要的。有兩種方式。

? ? ? ? 如果有可能,使用非靜態字體,并預先配置好對所需字形的支持。這通常適用于對字符有非常好限制的UI,比如說只有拉丁文/ASCII字符,或者其他一個較小的范圍。

? ? ? ? ?如果必須支持一個非常大范圍的字符,比如全部的萬國碼,字體必須被設為動態字體。為避免可預見的性能問題,主要的字體字形圖集在一開始就要通過Font.RequestCharactersInTexture設置適當的字符。

? ? ? ? 請記住,字體圖集的重建是是在每個UI Text組件被改變時才會被單獨觸發。當生成極多text組件時,在text組件內容和主要的字體圖集中收集所有獨特的字符是有好處的。這將會確認字形圖集只需要被重建一次而不是每次新字形生成時都會要重建。

? ? ? ? 另外請注意,當一個字體圖集的重建被觸發時,任何目前不包含在激活的UI Text組件中的字符將不會被添加到新的圖集中,雖然他們一開始調用Font.RequestCharactersInTexture被添加到圖集中。要解決此限制,請訂閱Font.textureRebuilt這個委托并且查詢Font.characterInfo來確保所有期望的字符保持在圖集中。

? ? ? ? Font.textureRebuilt委托目前沒有公開,它是一個單參數的Unity事件,參數是紋理圖集要被重建的字體,此事件的訂閱者應當遵循以下簽名:public void TextureRebuiltCallback(Font rebuiltFont) { /* ... */ }


專門的字形渲染器

由于字形眾所周知的情況,在每個字形位置相對固定的情況下,為這些要顯示的sprite寫一個用戶自定義的組件來顯示這些字形明顯是更有益的,比分顯示就是一個例子。

對于比分,要顯示的字符就是從眾所周知的數字0-9中繪制,不會改變范圍,并且以彼此固定的距離顯示。將一個整數分解為數碼并且顯示合適的數碼sprite是相對來說無關緊要的。這種專門的數字顯示系統以一種無需配置和非常快的計算、動畫和顯示速度來構建,比Canvas的UI Text組件強很多。

后備字體和內存使用

對于必須支持大字符集的應用程序,很容易在字體導入器的“Font Names”字段中列出大量的字體。列在 “Font Names” 字段內的任何字體將會作為后備字體,在字形不能在主要字體中找到的時候。后備字體的排列順序決定于它們列在“Font Names” 字段內的順序。

然而,為了支持這種行為,Unity會將“Font Names”字段中列出的所有字體加載到內存中。如果一個字體的字符集非常大,則后備字體的內存消耗將過多。這種情況經常發生在包括象形文字的字體,比如日本漢字或者是中文字符。

Best Fit和性能

一般來說,永遠不要使用UI text組件的Best Fit設置。

Best Fit動態的適配一個字體的最大整數大小,使text組件能夠顯示在框體中不會超出框體,并限定一個可配置的最大/最小的字體尺寸。然而,由于Unity會把每個要顯示的獨立的字形的每個單獨的尺寸渲染進字體圖集,所以使用Best Fit的話字體圖集將會被迅速的被不同的尺寸的字形所填滿。

從Unity5.3開始,Best Fit使用的尺寸檢測是非最佳的。它將每一個測試的增加尺寸的字形生成到字體圖集中,這遠遠增加了生成字體圖集所要花費的時間。這也傾向于導致圖集溢出,舊字形被踢出圖集。由于Best Fit計算所需大量測試,這會經常將別的text組件使用的字形驅逐,在合適的字體大小被計算出來以后,字體圖集的重建將會被強制執行至少一次。這個特別的問題在Unity5.4中被改正,并且Best Fit不會不必要的增加字體紋理圖集,但它仍舊比靜態大小的text要慢的多。

頻繁的字體圖集重建會迅速降低運行時性能,并導致內存碎片。設置為Best Fit的text組件的數量越多,這個問題就越嚴重。

Scroll View

在填充率問題之后,UGUI的Scroll View是第二個運行時的最常見性能問題的源頭。Scroll View通常需要大量的UI元素來表現其內容。Scroll View的填充有兩個基本的方法:

1.用Scroll View內容的所有要顯示的必要的元素來填充。

2.將元素存儲起來,當它們需要表現可視內容的時候重新給他們設定合適的位置。

這兩種解決方案都有問題。

第一個解決方案需要更多的時間來生成所有UI元素,而且隨著要顯示的元素的增加,Scroll View的rebuilt的所需時間也會跟著增加。如果Scroll View中只有少量的元素,比如Scroll View只需顯示一些Text組件,那么使用這個解決方案簡單易行,最為合適。

第二個解決方案需要大量的代碼來實現正確的當前要顯示的UI和layout系統。下面將進一步詳細討論兩種可行的解決方案。對于那些大量的復雜的要滾動的UI來說,通常需要使用某些對象池的方法來避免性能問題。

盡管存在這些問題,所有的方法都能夠通過給Scroll View添加RectMask2D組件來得到改進。這個組件可以確保Scroll View的viewport之外的元素不包括在要繪制的元素列表中,在列表中的元素在Canvas的rebuild的時候必須生成它們的幾何體,排列,并且進行分析。

簡單的Scroll View元素池

實現Scroll View對象池最簡單同時保留Unity內置的Scroll View組件的大多數原生便捷性的方法是使用一種混合的方法:

在UI中布局元素,這將允許布局系統來正確計算Scroll View的content大小,并且允許scrollbar正常工作,使用掛載Layout Element組件的游戲物體來作為可見UI元素的“placeholders(占位符)”。

然后,實例化足以填充Scroll View的可視區域的可視部分的可見UI元素,并且設定父物體為定位好的占位符。當Scroll View滾動時,重用UI元素來顯示滾動到視口中的內容。

這將大大減少必須要batch的UI元素的數量,因為batch的開銷增長只基于Canvas內的Canvas Renderer數量,而不是Rect Transform的數量。

解決問題的簡單方法

目前,當任何UI元素重新設置父級,或者層級排序變換的時候,該元素及其所有的子元素都將被標“Dirty”,并且會強制rebuild其Canvas。

這個問題的原因是Unity沒有分離重新設置父級和改變其層級排序的回調。這些事件都會觸發一個叫OnTransformParentChanged的回調。在UGUI源碼的Graphic類中(見源碼中的Graphic.cs腳本),這個回調實現并執行了SetAllDirty方法。通過將Graphic標Dirty,系統可以確保Graphic將在下一幀渲染前重建其布局和頂點。

可以將Canvas分配到Scroll View中每一個元素的根RectTransform上,這將會限制重建發生的范圍只在重新設置父級的元素上而不是Scroll View的整個content。但是這也會增加需要渲染Scroll View的drawcall數量。此外,如果Scroll View內獨立的元素是復雜的并且包含了十多個Graphic組件,特別是每個元素上包含了大量的Layout組件,其rebuild的開銷也經常可以會高到顯著的降低低端設備的幀率。

如果一個Scroll View的UI元素的大小是不可變的,那么完全重新計算布局和頂點就是不必要的。然而,避免這種問題的解決方案需要實現的對象池是基于位置的改變而不是重新設置父級或是更改層級順序。

基于位置改變的Scroll View池

為了避免上述問題,可以創建一個僅僅是移動其包含的UI元素的RectTransform的對象存儲池。這樣就通過移動RectTransform避免了rebuild尺寸未改變的內容,顯著的提高了Scroll View的性能。

要做到這一點,通常最好是要寫一個用戶自定義的Scroll View子類和寫一個用戶自定義的Layout Group組件。后者通常是更簡單的解決方案,可以通過實現UGUI的LayoutGroup的抽象基類來完成。

用戶自定義的LayoutGroup可以分析底層的元數據來檢查要顯示多少數據元素,并且可以重新設置Scroll View的content的RectTransform為合適的。也可以通過訂閱Scroll View change events(onValueChanged)并相應的使用來重新設置可見元素的位置。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容