1. 程式人生 > >NGUI開發優化技巧(下)

NGUI開發優化技巧(下)

NGUI更新開銷:NGUI進行自身的資料更新以及生成網格的過程中所產生的開銷,目前UI部分的CPU耗時大部分都是這部分的開銷;

NGUI的更新開銷幾乎全部來源於UIPanel.LateUpdate中;一般超過3~4ms,說明這部分更新開銷較高;

UI更新的數量和頻率通過合理的佈局,可以把這部分的開銷限制在很多小的範圍裡面;

在關卡的切換或點選一些UI介面時,可以出現一些較高的峰值;

但在戰鬥部分要保證UIPanel.LateUpdate在很小的範圍內波動,一般在2~3ms

UIPanel.LateUpdate在進行完全的重建時,才會出現較高的堆記憶體開銷;


NGUI更新的對渲染的影響:

NGUI的更新在渲染時的耗時出現在MeshRenderer.RenderMesh.DrawVBOMesh.CreateVBO有一部分是由NGUI造成的,NGUIUIPanel的重新整理和UI元素的變動導致網格發生了變化或更新,都會使Mesh.Create產生開銷;


UIPanel.LateUpdate的總堆記憶體分配在10~20M之間,是合理的;(10000~20000幀)

UIPanel的堆記憶體是有UIPanel的重新整理引起的,堆記憶體越高說明UIPanel的重新整理頻率越高,所涉及的UI元素的數量可能越大;

UILabel的更新開銷

1Font.CacheFontForText

2Shadow

3Outline



在手機上,紋理的大小會有所限制,紋理的內容會不停地變動,紋理不停地重新整理,會導致

Font.CacheFontForText不停地被觸發;

出現文字比較大,首次出現,並且用的是動態字型,就會產生比較高的Font.CacheFontForText函式開銷,對於頻繁地、反覆使用的文字,儘可能地做成靜態字型;



開啟和不開啟Shadow的區別;



開啟和不開啟Outline8的區別;(比Shadow的效能開銷還大

對於頻繁移動的文字,開啟Effect效果,對效能開銷比較大;

對於動態的文字(會移動或會變顏色),儘可能少用這些特效,對於靜態的不影響;(不會每一幀去更新網格)

UISprite的更新開銷

(Size越大,平鋪地越多,三角面片和網格數越多,造成開銷越大


使用和不使用Tiled模式的區別;

UISprite或文字的UI元素本身在移動或變顏色時,才會真正產生開銷;靜止時則不會更新,

不產生開銷;

UIPanel更新機制

1:更新單個UIDrawCall

2:更新所有的UIDrawCall

UIDrawCall:一個UIPanel中可能會有各種各樣的UI元素,這些UI元素可能來自於不同的圖集,對於來自於不同圖集的元素最終渲染時不能合在同一個DrawCall裡面,所以一個UIPanel裡面會包含很多個UIDrawCall,這些UIDrawCall首先按照UI元素的深度(Depth)進行排序,

然後再根據相鄰元素是否來自於同一個圖集來進行合併,這裡的每一個UIDrawCall都包含了若干個UI元素,這些UI元素來自於同一個圖集,且深度值在排序裡面是相鄰的;

進行Panel更新時,有兩種模式:

1:更新單個UIDrawCall:在更新過程中,某個UI元素進行了移動,只會更新自己所在的UI元素裡面的網格,不會影響其他的DrawCall

2:更新所有UIDrawCall:一旦滿足某種條件,會直接重建UIPanel裡面所有的DrawCall

 





並不是把DrawCall降低地越低越好,當一個頂點發生變化時,因為這個頂點和其他的頂點在同一個Mesh上面,所以一個頂點的更新會導致所有Mesh的更新;

如果新增的介面並不是和變動的UI元素在同一個DrawCall裡面,那麼在更新元素時,並不會產生影響;

總結

對於頻繁變動的UI元素,其所在的UIDrawCall面片數越少越好;

更新所有的UIDrawCall

主要原因:UIPanel中的UIDrawCall發生了變化;(在UIPanel裡面添加了DrawCall或是把UIPanel裡面原有的DrawCall拆分成多個(動態地新增或刪除一些UI元素))



在這一幀內,有很多網格進行了重建,會影響到Mesh.CreateVBO

動態新增視窗,穿插了NGUI Panel,導致UIPanel的完全重建;


將其中一個UIDrawCall分離到其他的UIPanel中後

不同UIPanel下的UI元素是不能進行合併的;

調整深度,不要打斷原有的DrawCall

動態的UI元素中少放東西,或直接把它獨立成一個UIPanel

更新所有UIDrawCall的條件

1:新增/刪除元素時,穿插了其他的UIDrawCall

2:新增/刪除的元素自成一個UIDrawCall

總結:

1:對於頻繁變動的UI元素,其所在的UIDrawCall面片數越少越好;

2:動態新增UI元素時,注意Depth的設定,儘可能合入已有的UIDrawCall

(在新增UI元素時,這個Depth是包含在現有的DrawCall裡面的,並且這個DrawCall的材質和所新增的元素的材質是一致的,這樣就不會產生完全的重建)

3:對於需要動態新增UI元素的UIPanel,減少其複雜度,從而避免在更新所有

UIDrawCall時開銷過大;(戰鬥過程中出現的連擊提示,應儘可能地減小這種介面的複雜度,

不要把靜態的東西(頭像、置頂的怪物的血條)也放在UIPanel裡面)

動態字型(字型在不停變換時):在手機上,文字所對應的字型、材質、紋理會不停地進行更新,每一次想往紋理裡面新增新的文字,當發現紋理不夠時,會觸發重新整理的操作,會根據當前建立紋理大小的限制(比如需要建立512*512,但根據某些機制發現只能建立256*256的),則會先把螢幕上啟用狀態的文字填入紋理,對於出現過的,但是已經被禁用的文字就會全部被剔除掉,這個過程相當於完全更新了紋理裡面的內容,這種操作也是耗時最大的操作;

動態字型優化的兩種方式:

1:如果字型可以做成靜態的,則優先做成靜態的,就可以避免Font.CacheFontForText

但也會出現一些問題(比如:在一些劇情比較複雜的遊戲中,涉及的劇情非常多,每一段劇情需要的文字量非常大,如果做成靜態的,可能帶來10多張1024*2048這種紋理,這就不同通過轉成靜態字型這種方式來處理這種情況);

2:文字只出現一次,後面不會出現,可以讓它預先出現,比如建立一個Label,裡面有這個文字,並且是處於啟用狀態的,只是相機看不到,可以把這些峰值移動到可以卡頓的地方,在真正播放劇情時,通過移動或者修改Layer讓這些文字更流暢地逐漸顯示出來;

進入讀條時,先把這些文字放成啟用狀態,讓動態字型需要的紋理先生成好;

多個動態的物體,放在一個Panel好,還是放在多個Panel好;

通常進行分組比較好,可以少量地提高合併網格的計算量(優化量並不多,因為網格總量是一樣的,只是分成幾個分組而已),影響比較大的是:Camera.Render裡面的CreateVBO

UI元素的分離時有一個好處:

一個螢幕上存在多個UI元素,只有少量的UI元素在移動,可以做動態Panel的劃分,對於靜態不動的網格不應該去更新;