Unity3D 移動開發代碼優化

1. 儘量避免每幀處理

比如:

function Update() { DoSomeThing(); }

可改爲每5幀處理一次:

function Update() { if(Time.frameCount % 5 == 0) { DoSomeThing(); } }

2. 定時重複處理用 InvokeRepeating 函數實現

比如,啓動0.5秒後每隔1秒執行一次 DoSomeThing 函數:

 

function Start() { InvokeRepeating("DoSomeThing", 0.5, 1.0); }

 

3. 優化 Update, FixedUpdate, LateUpdate 等每幀處理的函數

函數裏面的變量儘量在頭部聲明。

比如:

function Update() { var pos: Vector3 = transform.position; }

可改爲

private var pos: Vector3; function Update(){ pos = transform.position; }

 

4. 主動回收垃圾

給某個 GameObject 綁上以下的代碼:

function Update() { if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }

 

5. 運行時儘量減少 Tris 和 Draw Calls

預覽的時候,可點開 Stats,查看圖形渲染的開銷情況。特別注意 Tris 和 Draw Calls 這兩個參數。

一般來說,要做到:

Tris 保持在 7.5k 以下

Draw Calls 保持在 20 以下

 

6. 壓縮 Mesh

導入 3D 模型之後,在不影響顯示效果的前提下,最好打開 Mesh Compression。

Off, Low, Medium, High 這幾個選項,可酌情選取。

 

7. 避免大量使用 Unity 自帶的 Sphere 等內建 Mesh

Unity 內建的 Mesh,多邊形的數量比較大,如果物體不要求特別圓滑,可導入其他的簡單3D模型代替。

 

8. 優化數學計算

比如,如果可以避免使用浮點型(float),儘量使用整形(int),儘量少用複雜的數學函數比如 Sin 和 Cos 等等

 

減少固定增量時間

將固定增量時間值設定在0.04-0.067區間(即,每秒15-25幀)。您可以通過Edit->Project Settings->Time來改變這個值。這樣做降低了FixedUpdate函數被調用的頻率以及物理引擎執行碰撞檢測與剛體更新的頻率。如果您使用了較低的固定增量時間,並且在主角身上使用了剛體部件,那麼您可以啓用插值辦法來平滑剛體組件。

減少GetComponent的調用

使用 GetComponent或內置組件訪問器會產生明顯的開銷。您可以通過一次獲取組件的引用來避免開銷,並將該引用分配給一個變量(有時稱爲"緩存"的引用)。例如,如果您使用如下的代碼:

function Update () {

transform.Translate(0, 1, 0);

 

}

通過下面的更改您將獲得更好的性能:

 

var myTransform : Transform;

function Awake () {

myTransform = transform;

}

function Update () {

myTransform.Translate(0, 1, 0);

}

 

避免分配內存

您應該避免分配新對象,除非你真的需要,因爲他們不再在使用時,會增加垃圾回收系統的開銷。您可以經常重複使用數組和其他對象,而不是分配新的數組或對象。這樣做好處則是儘量減少垃圾的回收工作。同時,在某些可能的情況下,您也可以使用結構(struct)來代替類(class)。這是因爲,結構變量主要存放在棧區而非堆區。因爲棧的分配較快,並且不調用垃圾回收操作,所以當結構變量比較小時可以提升程序的運行性能。但是當結構體較大時,雖然它仍可避免分配/回收的開銷,而它由於"傳值"操作也會導致單獨的開銷,實際上它可能比等效對象類的效率還要低。

 

最小化GUI

使用GUILayout 函數可以很方便地將GUI元素進行自動佈局。然而,這種自動化自然也附帶着一定的處理開銷。您可以通過手動的GUI功能佈局來避免這種開銷。此外,您也可以設置一個腳本的useGUILayout變量爲 false來完全禁用GUI佈局:

function Awake () {

useGUILayout = false;

}

 

使用iOS腳本調用優化功能

UnityEngine 命名空間中的函數的大多數是在 C/c + +中實現的。從Mono的腳本調用 C/C++函數也存在着一定的性能開銷。您可以使用iOS腳本調用優化功能(菜單:Edit->Project Settings->Player)讓每幀節省1-4毫秒。此設置的選項有:

Slow and Safe – Mono內部默認的處理異常的調用

 

Fast and Exceptions Unsupported –一個快速執行的Mono內部調用。不過,它並不支持異常,因此應謹慎使用。它對於不需要顯式地處理異常(也不需要對異常進行處理)的應用程序來說,是一個理想的候選項。

 

優化垃圾回收

 

如上文所述,您應該儘量避免分配操作。但是,考慮到它們是不能完全杜絕的,所以我們提供兩種方法來讓您儘量減少它們在遊戲運行時的使用:

如果堆比較小,則進行快速而頻繁的垃圾回收

這一策略比較適合運行時間較長的遊戲,其中幀率是否平滑過渡是主要的考慮因素。像這樣的遊戲通常會頻繁地分配小塊內存,但這些小塊內存只是暫時地被使用。如果在iOS系統上使用該策略,那麼一個典型的堆大小是大約 200 KB,這樣在iPhone 3G設備上,垃圾回收操作將耗時大約 5毫秒。如果堆大小增加到1 MB時,該回收操作將耗時大約 7ms。因此,在普通幀的間隔期進行垃圾回收有時候是一個不錯的選擇。通常,這種做法會讓回收操作執行的更加頻繁(有些回收操作並不是嚴格必須進行的),但它們可以快速處理並且對遊戲的影響很小:

if (Time.frameCount % 30 == 0)

{

System.GC.Collect();

}

 

但是,您應該小心地使用這種技術,並且通過檢查Profiler來確保這種操作確實可以降低您遊戲的垃圾回收時間

如果堆比較大,則進行緩慢且不頻繁的垃圾回收

這一策略適合於那些內存分配 (和回收)相對不頻繁,並且可以在遊戲停頓期間進行處理的遊戲。如果堆足夠大,但還沒有大到被系統關掉的話,這種方法是比較適用的。但是,Mono運行時會儘可能地避免堆的自動擴大。因此,您需要通過在啓動過程中預分配一些空間來手動擴展堆(ie,你實例化一個純粹影響內存管理器分配的"無用"對象):

 

function Start() {

 

var tmp = new System.Object[1024];

 

// make allocations in smaller blocks to avoid them to be treated in a special way, which is designed for large blocks

 

for (var i : int = 0; i < 1024; i++)

 

tmp[i] = new byte[1024];

 

// release reference

 

tmp = null;

 

}

 

遊戲中的暫停是用來對堆內存進行回收,而一個足夠大的堆應該不會在遊戲的暫停與暫停之間被完全佔滿。所以,當這種遊戲暫停發生時,您可以顯式請求一次垃圾回收:

 

System.GC.Collect();

 

另外,您應該謹慎地使用這一策略並時刻關注Profiler的統計結果,而不是假定它已經達到了您想要的效果。

原文鏈接:http://blog.sina.com.cn/s/blog_6ad33d35010136fl.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章