Unity效能優化之程式碼優化
對於Unity效能優化,目前接觸到的大概有這幾個方面:
1. Draw Call
2. 資源(模型、貼圖、粒子)
3. 渲染(相機、光照、Shader)
4. 網路
5. 程式碼(程式碼編寫、資源載入、物理系統)
可以在Unity自帶的Profiler視窗檢視專案效能消耗主要在哪幾個地方,然後有針對性的進行優化。
作為一個程式,這裡跟大家分享一下最近常用到程式碼方面的一點技巧,如有不足之處,歡迎大大們提出寶貴意見~
1. 在場中有大量物體頻繁的啟用或隱藏時,不使用SetActive(),在需要隱藏時移到螢幕外 ,顯示時再移到螢幕內,即修改transform.position。還有一個方法,把需要隱藏的物體設為一個已隱藏的物體的子物體,因為父物體是未啟用狀態,子物體會自動隱藏,不過這種方法消耗與SetActive()差不多,不推薦使用。
下面是測試程式碼:
輸出結果:
SetActive(true)消耗最大,SetActive(false)與transform.parent其次,transform.position消耗最小,佔用的時間可以忽略不計。
2. 動態例項化到場景的物體,名字都會有一個字尾(Clone),有時候為了方便識別,會修改其名字,同樣會產生效能消耗。比如:
3. 在不影響正常執行結果的情況下,減少Update或FixUpdate的呼叫次數
假設現在將所有的Updata重新整理邏輯寫在DoUpdata中
3.1 每隔一定數量幀,執行一次DoUpdata
3.2 使用協程While(true)迴圈,每次迴圈間隔一定時間,呼叫DoUpdata,需要在Start函式中開啟協程
3.3 使用InvokeRepeating迴圈呼叫DoUpdata
4. 使用物件池
操作目標相對較少,可簡化物件池,實現效果
如果目標物件較多,就需要寫一個正經的物件池了,因為之前寫過,這裡就直接給連結啦
5. 音效播放時,為避免頻繁建立、銷燬播放器,可以對音效統一管理,同樣之前寫過,上鍊接~
6. 場景中經常需要動態生成GameObject,當一次建立的數量較多時,在不影響使用的情況下,可以使用協程,在多幀內完成建立,可參照:
7. 部分簡單的物理計算可以不使用Unity提供的物理系統,簡化物理計算量,下面是關於向量計算的一個栗子:
線段(向量)的計算(判斷線段重疊、相交,合併線段,點與線的關係)_004
8. 選擇合理的資料結構儲存資料。
在資料結構轉換時,可以避免如下的for迴圈,利用C#提供的方法
C#中 Array / List / Dictionary之相互轉換_014
9. 其他
9.1 儘量避免在Update和for迴圈內建立臨時變數。
9.2 儘量避免建立臨時字串。
9.3 可以使用for迴圈的情況,就不用foreach。
9.4 每個繼承MonoBehaviour的類,都會自動生成Update方法,但很多類是用不到Update的,這時候需要將其刪除,畢竟實時呼叫空方法,多少還是有消耗的。
9.5 數值計算中使用乘法而不用觸發,比如 a / 2, 可以寫成 a * 0.5f。
9.6 比較他Tag值時,使用if(gameObject.CompareTag("Tag")),而不是if(gameObject.tag == "Tag")。
9.7 開發過程中會各種Debug,這也會有一定的消耗,可以對Debug進行封裝,設定一個bool值,不過這樣console面板雙擊不會跳轉到呼叫Debug的程式碼行,最好的做法是將其做成dll檔案。
10、務必刪除指令碼中為空或不需要的預設方法;
11、只在一個指令碼中使用OnGUI方法;
12、避免在OnGUI中對變數、方法進行更新、賦值,輸出變數建議在Update內;
13、同一指令碼中頻繁使用的變數建議宣告其為全域性變數,指令碼之間頻繁呼叫的變數或方法建議宣告為全域性靜態變數或方法;
14、不要去頻繁獲取元件,將其宣告為全域性變數;
15、陣列、集合類元素優先使用Array,其次是List;
16、指令碼在不使用時指令碼禁用之,需要時再啟用;
17、可以使用Ray來代替OnMouseXXX類方法;
18、需要隱藏/顯示或例項化來回切換的物件,儘量不要使用SetActiveRecursively或active,而使用將物件遠遠移出相機範圍和移回原位的做法;
19、儘量少用模運算和除法運算,比如a/5f,一定要寫成a*0.2f。
20、對於不經常呼叫或更改的變數或方法建議使用Coroutines & Yield;
21、儘量直接宣告指令碼變數,而不使用GetComponent來獲取指令碼; iPhone
22、儘量使用整數數字,因為iPhone的浮點數計算能力很差;
23、不要使用原生的GUI方法;
24、不要例項化(Instantiate)物件,事先建好物件池,並使用Translate“生成”物件;
二、模型方面
01、合併使用同貼圖的材質球,合併使用相同材質球的Mesh;
02、角色的貼圖和材質球只要一個,若必須多個則將模型離分離為多個部分;
02、骨骼系統不要使用太多;
03、當使用多角色時,將動畫單獨分離出來;
04、使用層距離來控制模型的顯示距離;
05、陰影其實包含兩方面陰暗和影子,建議使用實時影子時把陰暗效果烘焙出來,不要使用燈光來調節光線陰暗。
06、少用畫素燈和使用畫素燈的Shader;
08、如果硬陰影可以解決問題就不要用軟陰影,並且使用不影響效果的低解析度陰影;
08、實時陰影很耗效能,儘量減小產生陰影的距離;
09、允許的話在大場景中使用線性霧,這樣可以使遠距離物件或陰影不易察覺,因此可以通過減小相機和陰影距離來提高效能;
10、使用圓滑組來儘量減少模型的面數;
11、專案中如果沒有燈光或物件在移動那麼就不要使用實時燈光;
12、水面、鏡子等實時反射/折射的效果單獨放在Water圖層中,並且根據其實時反射/折射的範圍來調整;
13、碰撞對效率的影響很小,但碰撞還是建議使用Box、Sphere碰撞體;
14、建材質球時儘量考慮使用Substance;
15、儘量將所有的實時反射/折射(如水面、鏡子、地板等等)都集合成一個面;
16、假反射/折射沒有必要使用過大解析度,一般64*64就可以,不建議超過256*256;
17、需要更改的材質球,建議例項化一個,而不是使用公共的材質球;
18、將不須射線或碰撞事件的物件置於IgnoreRaycast圖層;
19、將水面或類似效果置於Water圖層
20、將透明通道的物件置於TransparentFX圖層;
21、養成良好的標籤(Tags)、層次(Hieratchy)和圖層(Layer)的條理化習慣,將不同的物件置於不同的標籤或圖層,三者有效的結合將很方便的按名稱、類別和屬性來查詢;
22、通過Stats和Profile檢視對效率影響最大的方面或物件,或者使用禁用部分模型的方式檢視問題到底在哪兒;
23、使用遮擋剔除(Occlusion Culling)處理大場景,一種較原生的類LOD技術,並且能夠“分割”作為整體的一個模型。
三、其它 場景中如果沒有使用燈光和畫素燈,就不要使用法線貼圖,因為法線效果只有在有光源(Direct Light/Point Light/Angle Light/Pixel Light)的情況下才有效果。
2.1渲染
1.不使用或少使用動態光照,使用light mapping和light probes(光照探頭)
2.不使用法線貼圖(或者只在主角身上使用),靜態物體儘量將法線渲染到貼圖
3.不適用稠密的粒子,儘量使用UV動畫
4.不使用fog,使用漸變的面片(參考shadow gun)
5.不要使用alpha –test(如那些cutout shader),使用alpha-blend代替
6.使用盡量少的material,使用盡量少的pass和render次數,如反射、陰影這些操作
7.如有必要,使用Per-Layer Cull Distances,Camera.layerCullDistances
8.只使用mobile組裡面的那些預置shader
9.使用occlusion culling
11.遠處的物體繪製在skybox上
12.使用drawcall batching: 對於相鄰動態物體:如果使用相同的shader,將texture合併 對於靜態物體,batching要求很高,詳見Unity Manual>Advanced>Optimizing Graphics Performance>Draw Call Batching 規格上限
1. 每個模型只使用一個skinned mesh renderer
2. 每個mesh不要超過3個material
3. 骨骼數量不要超過30
4. 面數在1500以內將得到好的效率
2.2物理
1.真實的物理(剛體)很消耗,不要輕易使用,儘量使用自己的程式碼模仿假的物理
2.對於投射物不要使用真實物理的碰撞和剛體,用自己的程式碼處理
3.不要使用mesh collider
4.在edit->project setting->time中調大FixedTimestep(真實物理的幀率)來減少cpu損耗
2.3指令碼編寫
1.儘量不要動態的instantiate和destroy object,使用object pool
2.儘量不要再update函式中做複雜計算,如有需要,可以隔N幀計算一次
3.不要動態的產生字串,如Debug.Log("boo" + "hoo"),儘量預先建立好這些字串資源
4.cache一些東西,在update裡面儘量避免search,如GameObject.FindWithTag("")、GetComponent這樣的呼叫,可以在start中預先存起來
5.儘量減少函式呼叫棧,用x = (x > 0 ? x : -x);代替x = Mathf.Abs(x)
6.String的相加操作,會頻繁申請記憶體並釋放,導致gc頻繁,使用System.Text.StringBuilder代替
2.4 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等
2.4 GUI
1.不要使用內建的onGUii函式處理gui,使用其他方案,如NGUI