1. 程式人生 > >Unity效能優化彙總

Unity效能優化彙總

內容會持續更新,有錯誤的地方歡迎指正,謝謝!

效能優化嘛,大家都說像海綿中的水,擠一擠總會有的,但我卻想說它像內衣裡的肉,擠一擠真的可以有。哈哈哈,有點汙,見笑了,進入正題。

CPU方面的優化

CPU優化不夠會出現的問題:

  1. 由於短時間內的計算量太大,導致畫面流暢性降低,俗稱跳幀
  2. 發熱嚴重,耗電量高

CPU優化方向:

  1. DrawCalls
  2. 物理元件
  3. GC(GC為處理記憶體,此項為CPU使用GC處理記憶體時產生的效能損耗)
  4. 程式碼

Drawcalls

Drawcall是啥?其實就是對底層圖形程式(比如:OpenGL ES)介面的呼叫,以在螢幕上畫出東西。

如何優化:

1)使用Draw Call Batching,也就是描繪呼叫批處理。Unity在執行時可以將一些物體進行合併,從而用一個描繪呼叫來渲染他們。

  1. 靜態批處理Static Batching:只要是靜態不動的物體且具有相同材質的話就可以使用靜態批處理來降低描繪呼叫
  2. 動態批處理Dynamic Batching:動態批處理是引擎自動進行,無需設定,當物體共享相同的材質,則引擎就會自動對Drawcall進行優化,也就是動態批處理(如例項化預製體)。動態批處理存在約束,稍有不慎就會增加Drawcall

動態批處理有很多約束:頂點數、縮放、不同材質等約束都不會自動批處理,所以儘量使用靜態批處理。

2)UGUI需將同一介面的UI元素打包成圖集。

物理元件

1) 處理Rigidbody時,使用FixedUpdate,設定Fixed timestep(固定時間步),減少物理計算次數,運動起來也平滑,提高遊戲效能。

2)減少FPS,即減少每秒的幀數,在ProjectSetting-> Quality中的VSync Count 引數會影響你的FPS,EveryVBlank相當於FPS=60,EverySecondVBlank = 30;這兩種情況都不符合遊戲的FPS的話,或通過程式碼手動設定。
降低FPS的好處:
1.省電,減少手機發熱的情況;
2.能穩定遊戲FPS,減少出現卡頓的情況。

3)儘量不用MeshCollider

如果可以的話,儘量不用MeshCollider,以節省不必要的開銷。如果不能避免的話,儘量用減少Mesh的面片數,或用較少面片的來代替。

4)粒子元件,螢幕上最大粒子數量建議小於200個,並關閉粒子的碰撞功能。

GC

雖然GC是用來處理記憶體,即回收垃圾的,但是卻會增加CPU的開銷。首先我們要明確所謂的GC是Mono執行時的機制,而非引擎的機制,而它管理的也是Mono的託管堆,而非引擎的本機堆。所以,GC不是用來處理引擎的assets(紋理,音效等等)的記憶體釋放的,因為U3D引擎也有自己的記憶體堆而不是和Mono一起使用所謂的託管堆。其次我們要搞清楚什麼東西會被分配到託管堆上?就是引用型別咯。比如類的例項、字串、陣列等等;而作為int,float,包括結構體struct其實都是值型別,它們會被分配在堆疊上而非堆上。所以我們關注的物件無外乎就是類例項、字串、陣列這些了,所以GC的優化說白了也就是程式碼的優化。

此部分的程式碼優化只針對是否會觸發GC:

1)字串處理。如需多次用String的+運算子來拼接字串,就用StringBuilder的Append方法。

2)不要直接訪問gameobject的tag屬性。比如if (go.tag == “human”)最好換成if (go.CompareTag (“human”))。因為訪問物體的tag屬性會在堆上額外的分配空間。如果在迴圈中這麼處理,留下的垃圾就可想而知了。

3)使用物件池,以實現空間的重複利用。

4)最好不用LINQ的命令,因為它們會分配臨時的空間,同樣也是GC收集的目標。

程式碼

除了使用使用合理的演算法和資料結構外,還有如下需要注意:

2)不要頻繁使用GetComponent去頻繁獲取元件。如果要使用,可申明為全域性變數,並只需在Awake函式中GetComponent。

3)使用內建陣列如使用Vector3.zero而不是new Vector(0,0,0);

4)指令碼在不使用時,禁用之,需要時再啟用;

5)可以使用射線Ray來代替OnMouseXXX類方法

6)儘量少用模運算和除法運算,比如a/5f,一定要寫成a*0.2f

7)不要使用原生的GUI方法,即OnGUI函式

8)務必刪除指令碼中為空或不需要的預設方法

9)同一指令碼中頻繁使用的變數建議宣告其為全域性變數,指令碼之間頻繁呼叫的變數或方法建議宣告為全域性靜態變數或方法

10)儘量使用整數數字,因為iPhone的浮點數計算能力很差

11)將計算分到多個邏輯幀中進行計算,避免短時間內的效能超過負荷,俗稱“分幀”。

12)將可以快取的資料儘可能的快取起來,避免重複計算和重複分配記憶體。例如:不要重複例項化同一個物件,可以事先建好物件池

GPU的優化

GPU優化不夠會出現的問題:

  1. 發熱嚴重,耗電量高
  2. FPS降低

GPU優化方向:

  1. 畫素的複雜度:比如實時陰影(手遊中禁用),複雜的shader等等
  2. 頂點過多,即模型複雜面數多
  3. GPU的視訊記憶體頻寬

除了優化美術資源,包括合理規劃圖集、約定好模型的最大三角形面數、制定合理的粒子效果規範等;再除了使用平臺推薦的壓縮格式,比如安卓平臺的ETC1和IOS平臺的PVRTC。還有如下優化建議:

簡化或者優化著色器(Shader)

1)少使用的函式:pow,sin,cos等

2)fixed、half、float慢慢斟酌著使用,切忌浪費,採取夠用的型別就行

3)在遊戲開始前就對Shader進行編譯和載入。

減少繪製數目

拋開美術建模時應該注意頂點數和麵數不說,說說程式方面該如何處理:

1)保持材質的數目儘可能少。這使得Unity更容易進行批處理。

2)使用紋理圖集(一張大貼圖裡包含了很多子貼圖)來代替一系列單獨的小貼圖。它們可以更快地被載入,具有很少的狀態轉換,而且批處理更友好。

3)如果使用了紋理圖集和共享材質,使用Renderer.sharedMaterial 來代替Renderer.material 。

4)使用光照紋理(lightmap)而非實時燈光。

5)使用LOD(細節級別漸變),好處就是對那些離得遠,看不清的物體的細節可以忽略。也就是根據距離的遠近使用不同模型級別,這樣就可以減少模型上面的頂點和麵片數量從而提高效能。

6)遮擋剔除(Occlusion culling)

優化視訊記憶體頻寬

1)壓縮圖片,減小視訊記憶體頻寬的壓力。例如:可以通過減色的方式減少圖片大小。許多UI其實用的色彩很少,用不到256色,這類圖片就可以使用減色壓縮。

2)使用MipMap(紋理貼圖金字塔)貼圖會根據攝像機距離模型的遠近而調整不同的不同質量的貼圖顯示。
Mipmap中每一個層級的小圖都是主圖的一個特定比例的縮小細節的複製品。因為存了主圖和它的那些縮小的複製品,所以記憶體佔用會比之前大。但是為何又優化了視訊記憶體頻寬呢?因為可以根據實際情況,選擇適合的小圖來渲染。所以,雖然會消耗一些記憶體,但是為了圖片渲染的質量(比壓縮要好),這種方式也是推薦的。

記憶體的優化

記憶體優化不夠會出現的問題:

閃退和卡死,比如安卓的低記憶體殺手Low Memory Killer會在低記憶體情況下殺掉記憶體佔用過大的程式。

實際上Unity遊戲使用的記憶體一共有三種:

  1. 程式程式碼
  2. 託管堆(Managed Heap)
  3. 本機堆(Native Heap)。

除了動態載入和解除安裝資源,比如在遊戲內的時候,我們可以把遊戲外的一些UI圖集解除安裝掉;除了降低資源質量或螢幕解析度,這是有損優化,一般作為最後的手段。還有如下優化建議:

程式程式碼

程式程式碼包括了所有的Unity引擎使用的庫、以及你所寫的所有的遊戲程式碼。
在編譯後,得到的執行檔案將會被載入到裝置中執行,並佔用一定記憶體。這部分記憶體實際上是沒有辦法去“管理”的,它們將在記憶體中從一開始到最後一直存在。一個空的Unity預設場景,什麼程式碼都不放,在iOS裝置上佔用記憶體應該在17MB左右,而加上一些自己的程式碼很容易就飆到20MB左右。想要減少這部分記憶體的使用,能做的就是減少使用的庫,稍後再說。
也許,這就是,為什麼不用Unity高效開發APP的原因吧?但還是可以考慮用Unity開發APP,畢竟還好吧~

託管堆

託管堆是被Mono使用的一部分記憶體。Mono是一個開源的.net框架的一種實現,對於Unity開發,其實充當了基本類庫的角色。託管堆用來存放陣列、字串、類的例項等。“託管”的意思是Mono“應該”自動地改變堆的大小來適應你所需要的記憶體,並且定時地使用垃圾回收(GC)來釋放已經不需要的記憶體。

本機堆

本機堆是Unity引擎進行申請和操作的地方,比如貼圖,音效,關卡資料等。Unity使用了自己的一套記憶體管理機制來使這塊記憶體具有和託管堆類似的功能。基本理念是:如果在這個關卡里需要某個資源,那麼在需要時就載入,之後在沒有任何引用時進行解除安裝。
聽起來很美好也和託管堆一樣,但是由於Unity有一套自動載入和解除安裝資源的機制,讓兩者變得差別很大。自動載入資源可以為開發者省不少事兒,但是同時也意味著開發者失去了手動管理所有載入資源的權力,這非常容易導致大量的記憶體佔用(貼圖什麼的你懂的),也是Unity給人留下“吃記憶體”印象的罪魁禍首。

優化程式程式碼的記憶體佔用

這部分的優化相對簡單,因為能做的事情並不多:主要就是減少打包時的引用庫(即 庫剝離),改一改build設定即可。

  1. 當使用Unity開發時,預設的Mono包含庫可以說大部分用不上,在Player Setting面板裡,將最下方的Optimization欄目中“Api Compatibility Level”選為.NET 2.0 Subset,表示你只會使用到部分的.NET 2.0 Subset,不需要Unity將全部.NET的API包含進去。
  2. 接下來的“Stripping Level”表示從build的庫中剝離的力度,每一個剝離選項都將從打包好的庫中去掉一部分內容。你需要保證你的程式碼沒有用到這部分被剝離的功能,選為“Use micro mscorlib”的話將使用最小的庫。庫剝離可以極大地降低打包後的程式的尺寸以及程式程式碼的記憶體佔用。

實際上,在遊戲開發中絕大多數被剝離的功能使用不上的,因此不管如何,庫剝離的優化方法都值得一試。

IO和網路

IO和網路優化不好會出現的問題:

  1. 網路延遲甚至掉線
  2. 載入資源導致的跳幀
  3. 載入時間過長

常見的優化手段:

  1. 使用獨立的執行緒進行載入,Unity中還能利用協程
  2. 減少網路包裡面的冗餘資料
  3. 合併小包,減少請求資料的次數
  4. 分幀對回包進行處理
  5. 限制一定時間內的發包頻率

Unity官方優化建議

不用不是每個主流手機都支援的技術,就是如果可以不用就不用或有備選方案。

渲染

1.不使用或少使用動態光照,使用light mapping(光照貼圖)和light probes(光照探頭)
2.不使用法線貼圖(或者只在主角身上使用),靜態物體儘量將法線渲染到貼圖
3.不使用稠密的粒子,儘量使用Animation和UV動畫
4.不使用fog,使用漸變的面片(參考shadow gun)
5.不要使用alphatest,使用alphablend代替。移動端用alphablend代替alphatest實際是沒有辦法的辦法,並不是說完全可以做到alphatest的效果
6.使用盡量少的material,使用盡量少的pass和render次數,如反射、陰影這些操作
7.如有必要,使用Per-Layer Cull Distances(攝像機分層距離剔除)
8.只使用mobile組裡面的那些預置shader
9.使用occlusion culling(攝像機遮擋剔除)
10.遠處的物體繪製在skybox上
11.使用drawcall batching(動態批處理和靜態批處理):
對於相鄰動態物體:如果使用相同的shader,將texture合併
對於靜態物體,batching要求很高,詳見Unity Manual>Advanced>Optimizing Graphics Performance>Draw Call Batching
12.規格上限
每個模型只使用一個skinned mesh renderer(蒙皮的網格渲染器)
每個mesh不要超過3個material
骨骼數量不要超過30
面數在1500以內將得到好的效率

物理

1.真實的物理(剛體)很消耗,不要輕易使用,儘量使用自己的程式碼模仿假的物理
2.對於投射物不要使用真實物理的碰撞和剛體,用自己的程式碼處理
3.不要使用mesh collider
4.在edit->project setting->time中調大FixedTimestep(真實物理的幀率)來減少cpu損耗

指令碼編寫

1.儘量不要動態的instantiate和destroy object,使用object pool(物件池)
2.儘量不要在update函式中做複雜計算,如有需要,可以隔N幀計算一次
3.不要動態的產生字串,如Debug.Log(“boo” + “hoo”),儘量預先建立好這些字串資源
4.提前快取一些東西,在Update裡面儘量避免search,如GameObject.FindWithTag(“”)、GetComponent這樣的呼叫,可以在Start中預先存起來
5.儘量減少函式呼叫棧,用x = (x > 0 ? x : -x);代替x = Mathf.Abs(x)
6.下面的程式碼是幾個GC“噩夢”

不要在函式中動態new array,最好將一個array通過引數傳進函式裡修改。

shader編寫

1.資料型別

fixed / lowp - for colors, lighting information and normals,
half / mediump - for texture UV coordinates(座標),
float / highp - avoid in pixel shaders, fine to use in vertex shader for position calculations.

2.少使用的函式:pow,sin,cos等
最簡單的方法就是預先把所有值的結果儲存起來,比如一個數組

GUI

1.不要使用內建的OnGUI函式處理GUI,使用其他方案,如UGUI、NGUI。

格式

1.貼圖壓縮格式:ios上儘量使用PVRTC,android上使用ETC

效能分析工具

  1. Unity官方就提供了Unity Profiler和其他有針對性的分析工具
  2. 英偉達提供的效能測試工具
  3. 騰訊WeTest聯合Unity官方打造的效能分析工具UPA

總結

作為前端,你應當儘量少寫業務邏輯,你關注過一下的模組嗎?

效能:你有沒有在自己的遊戲中進行Profile,觀察在以上各個引數有沒有達到指標
安全:你的遊戲前端程式碼的Release版本是否還能被別人輕易反編譯,你的遊戲是否還能輕易被玩家擷取網路包或修改記憶體資料
工具:你能不能做出更優秀的工具來給美術和策劃使用,解放他們的生產力?

CPU、GPU、記憶體、IO網路這四個方面的優化總是相互制衡的,你把一個方面的優化做好了,另一個方面的問題又會出現了,比如,我們如果使用動態載入和解除安裝資源,這就雖然減少了記憶體佔用量,但會在IO上造成載入時間延長的問題。

所以,我們在做遊戲優化的時候,不能太追求完美,剛剛好就是闊以了,使得以上這四個方面能達到均衡即可,切忌在某一方面優化過頭。