引子
優(yōu)化Mono的托管內(nèi)存
Mono的托管內(nèi)存的優(yōu)化主要是代碼的優(yōu)化小染,以下部分是我在網(wǎng)上收集的資料:
內(nèi)容
1.盡量不要動態(tài)的Instantiate和Destroy Object絮蒿,使用Object Pool杈女。看看我之前寫的一篇文章就基本懂了豁遭!
2.盡量不要再Update函數(shù)中做復(fù)雜計算卧斟,如有需要社搅,可以隔N幀計算一次。
3.不要動態(tài)的產(chǎn)生字符串挚躯,如Debug.Log("boo" + "hoo")强衡,盡量預(yù)先創(chuàng)建好這些字符串資源。
4.Cache一些東西码荔,在update里面盡量避免search漩勤,如GameObject.FindWithTag("")、GetComponent這樣的調(diào)用缩搅,可以在Start中預(yù)先存起來越败。
5.盡量減少函數(shù)調(diào)用棧,用x = (x > 0 ? x : -x);代替x = Mathf.Abs(x)
6.下面的代碼是幾個GC“噩夢”誉己。 String的相加操作眉尸,會頻繁申請內(nèi)存并釋放,導(dǎo)致gc頻繁巨双,使用System.Text.StringBuilder代替
void ConcatExample(intArray: int[]) {
var line = intArray[0].ToString()噪猾;
for (i = 1; i < intArray.Length; i++) {
line += ", " + intArray[i].ToString();
}
return line;
}
7.在函數(shù)中動態(tài)new array,最好將一個array筑累、傳進(jìn)函數(shù)里修改
8.Update處理可改為每5幀處理一次:
void Update() { DoSomeThing(); }
void Update() { if(Time.frameCount % 5 == 0) { DoSomeThing();}}
9.定時重復(fù)處理用 InvokeRepeating 函數(shù)實現(xiàn) 比如袱蜡,啟動0.5秒后每隔1秒執(zhí)行一次 DoSomeThing 函數(shù):
void Start() { InvokeRepeating("DoSomeThing", 0.5, 1.0); }
10.優(yōu)化 Update, FixedUpdate, LateUpdate 等每幀處理的函數(shù) 函數(shù)里面的變量盡量在頭部聲明。比如:
void Update() { var pos: Vector3 = transform.position; }
可改為
private var pos: Vector3; void Update(){ pos = transform.position; }
11.主動回收垃圾,給某個 GameObject 綁上以下的代碼:
void Update() { if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }
12.優(yōu)化數(shù)學(xué)計算,比如:如果可以避免使用浮點型(float)慢宗,盡量使用整形(int)坪蚁,盡量少用復(fù)雜的數(shù)學(xué)函數(shù)比如 Sin 和 Cos 等等奔穿。
13.減少固定增量時間,將固定增量時間值設(shè)定在0.04-0.067區(qū)間(即敏晤,每秒15-25幀)贱田。您可以通過Edit->Project Settings->Time來改變這個值。這樣做降低了FixedUpdate函數(shù)被調(diào)用的頻率以及物理引擎執(zhí)行碰撞檢測與剛體更新的頻率嘴脾。如果您使用了較低的固定增量時間男摧,并且在主角身上使用了剛體部件,那么您可以啟用插值辦法來平滑剛體組件译打。
14.減少GetComponent的調(diào)用耗拓,使用 GetComponent或內(nèi)置組件訪問器會產(chǎn)生明顯的開銷。您可以通過一次獲取組件的引用來避免開銷奏司,并將該引用分配給一個變量(有時稱為"緩存"的引用)乔询。例如,如果您使用如下的代碼:
void Update () {
transform.Translate(0, 1, 0);
}
var myTransform : Transform;
void Awake () {
myTransform = transform;
}
void Update () {
myTransform.Translate(0, 1, 0);
}
15.避免分配內(nèi)存韵洋,您應(yīng)該避免分配新對象竿刁,除非你真的需要,因為他們不再在使用時麻献,會增加垃圾回收系統(tǒng)的開銷们妥。您可以經(jīng)常重復(fù)使用數(shù)組和其他對象,而不是分配新的數(shù)組或?qū)ο竺阄恰_@樣做好處則是盡量減少垃圾的回收工作监婶。同時,在某些可能的情況下齿桃,您也可以使用結(jié)構(gòu)(struct)來代替類(class)惑惶。這是因為,結(jié)構(gòu)變量主要存放在棧區(qū)而非堆區(qū)短纵。因為棧的分配較快带污,并且不調(diào)用垃圾回收操作,所以當(dāng)結(jié)構(gòu)變量比較小時可以提升程序的運行性能香到。但是當(dāng)結(jié)構(gòu)體較大時鱼冀,雖然它仍可避免分配/回收的開銷,而它由于"傳值"操作也會導(dǎo)致單獨的開銷悠就,實際上它可能比等效對象類的效率還要低千绪。
16.使用內(nèi)置數(shù)組,內(nèi)置數(shù)組是非常快的梗脾。ArrayList或Array類很容易使用荸型,你能輕易添加元件。但是他們有完全不同的速度炸茧。 內(nèi)置數(shù)組有固定長度瑞妇,并且大多時候你會事先知道最大長度然后填充它稿静。內(nèi)置數(shù)組最好的一點是他們直接嵌入結(jié)構(gòu)數(shù)據(jù)類型在一個緊密的緩存里,而不需要任何額外 類型信息或其他開銷辕狰。因此改备,在緩存中遍歷它是非常容易的,因為每個元素都是對齊的柳琢。
private var positions : Vector3[];
void Awake () {
positions = new Vector3[100];
for (var i=0;i<100;i++)
positions[i] = Vector3.zero;
}
17.使用靜態(tài)類型
使用靜態(tài)類型替代動態(tài)類型绍妨。Unity使用一種技術(shù)叫做類型推理的技術(shù)來自動轉(zhuǎn)換JavaScript為靜態(tài)類型腳本。
var foo = 5;
上面例子中的foo將自動被推斷為一個整數(shù)值柬脸。因此,Unity可能使用大量的編輯時間進(jìn)行優(yōu)化毙驯,而不使用耗時的動態(tài)名稱變量查找等倒堕。這就是為什么Unity的JavaScript執(zhí)行平均速度是其他JavaScript的20倍的原因之一。
唯一的問題是有時不是所有的東西都能做類型推斷爆价,Unity將會為這些變量重新使用動態(tài)類型垦巴。通過這樣,編寫JavaScript代碼很簡單铭段,但也會使代碼運行速度變慢骤宣。
看個例子:
void Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}
這里foo將是動態(tài)類型,因此呼叫函數(shù)DoSomething必須要較長的時間序愚,因為foo的類型未知憔披,它必須弄明白是否支持DoSomething函數(shù),如果支持爸吮,調(diào)用函數(shù)芬膝。
void Start ()
{
MyScript foo = GetComponent(MyScript);
foo.DoSomething();
}
這里我們強制foo為指定類型,你將獲得更好的性能形娇。
18.使用#pragma strict
現(xiàn)在問題是锰霜,你通常不會意識到你在使用動態(tài)類型。#pragma strict可以解決這個問題桐早!簡單的添加#pragma strict在腳本頂部癣缅,之后Unity將禁用腳本的動態(tài)類型,強制你使用靜態(tài)類型哄酝。如果有一個類型未知友存,Unity將報告編譯錯誤。下面炫七,foo將在編 譯時報錯:
#pragma strict
void Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}
19.緩存組件查找
另一個優(yōu)化是組件緩存爬立。這種優(yōu)化需要一些代碼并且不是總有必要。但是如果你的代碼真的很大万哪,并且你需要盡可能的性能提升侠驯,它會是很好的優(yōu)化抡秆。
當(dāng)你通過GetComponent獲取一個組件或一個變量時,Unity必須從游戲物體里找到正確的組件吟策。這時你便能通過一個緩存組件引用到一個私有變量儒士。
將:
void Update () {
transform.Translate(0, 0, 5);
}
//轉(zhuǎn)換為:
private var myTransform : Transform;
void Awake () {
myTransform = transform;
}
void Update () {
myTransform.Translate(0, 0, 5);
}
后面的代碼運行較快,因為Unity不用在每一幀尋找變換組件檩坚。同樣着撩,支持腳本組件。你可以使用GetComponent獲取組件或其他快捷屬性匾委。
20.如果沒有必要不要調(diào)用函數(shù)
最簡單拖叙,最好的優(yōu)化是執(zhí)行最少的工作。如赂乐,當(dāng)一個敵人在遠(yuǎn)處時薯鳍,讓他處于睡眠狀態(tài),大多時候是可行的挨措。直到玩家靠近挖滤,可以這樣處理:
void Update ()
{
// Early out if the player is too far away. if (Vector3.Distance(transform.position, target.position) > 100)
return;
perform real work work...
}
這 并不是很好的方法,雖然Unity不得不在每一幀訪問update函數(shù)浅役。更好的方法是禁用這個行為直到玩家靠近斩松。有3中方法做這個:使用 OnBecameVisible和OnBecameInvisible。這些調(diào)用與渲染系統(tǒng)相聯(lián)系觉既。一旦攝像機看到物體惧盹,OnBecameVisible 將被調(diào)用,不看他時奋救,OnBecameInvisible被調(diào)用岭参。這有時很有用。但是對于AI來講通常是沒有用的尝艘,因為你背轉(zhuǎn)敵人演侯,敵人就變成不可用了。
function OnBecameVisible () {
enabled = true;
}
function OnBecameInvisible ()
{
enabled = false;
}
21.使用觸發(fā)器背亥。一個簡單的球形觸發(fā)器能引發(fā)驚人效果秒际。你可以調(diào)用OnTriggerEnter/Exit,當(dāng)進(jìn)入你想要的作用范圍狡汉。
void OnTriggerEnter (Collider c)
{
if (c.CompareTag("Player"))
enabled = true;
}
void OnTriggerExit (Collider c)
{
if (c.CompareTag("Player"))
enabled = false;
}