unity3D 對(duì)于移動(dòng)平臺(tái)的支持無(wú)可厚非,但是也有時(shí)候用3D 開發(fā)出來(lái)的應(yīng)用、游戲在移動(dòng)終端上的運(yùn)行有著明顯的效率問題,比如卡、畫質(zhì)等各種問題。自己在做游戲開發(fā)的時(shí)候偶有所得。對(duì)于主要影響性能的因素做個(gè)總結(jié)。
主要因素有:
1. Saved by batching 值過大 ---- > 這個(gè)值主要是針對(duì)Mesh的批處理,這個(gè)值越高,應(yīng)用就越卡
2. Drawcall 值過大 ---- > Drawcall 值過大,所需要的 GPU 的處理性能較高,從而導(dǎo)致CPU的計(jì)算時(shí)間過長(zhǎng),于是就卡了
3. 點(diǎn)、面過多 ---- > 點(diǎn)、面過多,GPU 根據(jù)不同面的效果展開計(jì)算,并且CPU計(jì)算的數(shù)據(jù)也多,所以效果出來(lái)了,但是卡巴斯基
由于 Saved by batching 和 Drawcall 值過大所引起的卡的問題我所做的優(yōu)化方式有:
1. 對(duì)于模型 :Mesh 合并,有個(gè)不錯(cuò)的插件(DrawCall Minimizer ---> 直接上Asset Store 下載即可,免費(fèi)的,而且有文檔,很容易上手)
2. 對(duì)于UI : 盡量避免使用Unity3D自帶的 GUI 換用 NGUI或者EZGUI;因?yàn)檫@兩個(gè)UI插件對(duì)于UI中的圖片處理是將UI圖片放置在一個(gè) Atlas 中,一個(gè) Atlas 對(duì)應(yīng)一個(gè)Drawcall
3. 對(duì)于燈光: 可以使用 Unity3D 自帶的 Lightmapping 插件來(lái)烘焙場(chǎng)景中的燈光效果到物體材質(zhì)上
4. 對(duì)于場(chǎng)景: 可以使用 Unity3D 自帶的 Occlusion Culling 插件把靜止不動(dòng)的場(chǎng)景元素烘焙出來(lái)
4. 對(duì)于特效:盡量把材質(zhì)紋理合并
對(duì)于Unity3D 在移動(dòng)終端上支持的Drawcall 數(shù)到底多少,主要是跟機(jī)子性能有關(guān)的,當(dāng)然也不是說(shuō)值小性能就一定沒問題(本人親測(cè),也有17就卡的,主要是模型材質(zhì)紋理過大所引起的),目前我做的是70左右的,還OK,挺正常的
由于點(diǎn)、面過多所導(dǎo)致的性能問題,最好用簡(jiǎn)模,用四面體來(lái)做復(fù)雜的模型,但是面、點(diǎn)也別太多,至于Unity3D 到底支持多少點(diǎn)、面的說(shuō)法各異,我也搞不懂,總之少些肯定OK
檢測(cè)方式:
一,Unity3D 渲染統(tǒng)計(jì)窗口
Game視窗的Stats去查看渲染統(tǒng)計(jì)的信息:
1、FPS
fps其實(shí)就是 frames per second,也就是每一秒游戲執(zhí)行的幀數(shù),這個(gè)數(shù)值越小,說(shuō)明游戲越卡。
2、Draw calls
batching之后渲染mesh的數(shù)量,和當(dāng)前渲染到的網(wǎng)格的材質(zhì)球數(shù)量有關(guān)。
3、Saved by batching
渲染的批處理數(shù)量,這是引擎將多個(gè)對(duì)象的繪制進(jìn)行合并從而減少GPU的開銷;
很多GUI插件的一個(gè)好處就是合并多個(gè)對(duì)象的渲染,從而降低DrawCalls ,保證游戲幀數(shù)。
4、Tris 當(dāng)前繪制的三角面數(shù)
5、Verts 當(dāng)前繪制的頂點(diǎn)數(shù)
6、Used Textures 當(dāng)前幀用于渲染的圖片占用內(nèi)存大小
7、Render Textures 渲染的圖片占用內(nèi)存大小,也就是當(dāng)然渲染的物體的材質(zhì)上的紋理總內(nèi)存占用
8、VRAM usage 顯存的使用情況,VRAM總大小取決于你的顯卡的顯存
9、VBO Total 渲染過程中上載到圖形卡的網(wǎng)格的數(shù)量,這里注意一點(diǎn)就是縮放的物體可能需要額外的開銷。
10、Visible Skinned Meshes 蒙皮網(wǎng)格的渲染數(shù)量
11、Animations 播放動(dòng)畫的數(shù)量
注意事項(xiàng):
1,運(yùn)行時(shí)盡量減少 Tris 和 Draw Calls
預(yù)覽的時(shí)候,可點(diǎn)開 Stats,查看圖形渲染的開銷情況。特別注意 Tris 和 Draw Calls 這兩個(gè)參數(shù)。
一般來(lái)說(shuō),要做到:
Tris 保持在 7.5k 以下,有待考證。
Draw Calls 保持在 20 以下,有待考證。
2,F(xiàn)PS,每一秒游戲執(zhí)行的幀數(shù),這個(gè)數(shù)值越小,說(shuō)明游戲越卡。
3,Render Textures 渲染的圖片占用內(nèi)存大小。
4,VRAM usage 顯存的使用情況,VRAM總大小取決于你的顯卡的顯存。
二,代碼優(yōu)化
1. 盡量避免每幀處理
比如:
function Update() { DoSomeThing(); }
可改為每5幀處理一次:
function Update() { if(Time.frameCount % 5 == 0) { DoSomeThing(); } }
2. 定時(shí)重復(fù)處理用 InvokeRepeating 函數(shù)實(shí)現(xiàn)
比如,啟動(dòng)0.5秒后每隔1秒執(zhí)行一次 DoSomeThing 函數(shù):
function Start() { InvokeRepeating("DoSomeThing", 0.5, 1.0); }
3. 優(yōu)化 Update, FixedUpdate, LateUpdate 等每幀處理的函數(shù)
函數(shù)里面的變量盡量在頭部聲明。
比如:
function Update() { var pos: Vector3 = transform.position; }
可改為
private var pos: Vector3; function Update(){ pos = transform.position; }
4. 主動(dòng)回收垃圾
給某個(gè) GameObject 綁上以下的代碼:
function Update() { if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }
5. 優(yōu)化數(shù)學(xué)計(jì)算
比如,如果可以避免使用浮點(diǎn)型(float),盡量使用整形(int),盡量少用復(fù)雜的數(shù)學(xué)函數(shù)比如 Sin 和 Cos 等等
6,減少固定增量時(shí)間
將固定增量時(shí)間值設(shè)定在0.04-0.067區(qū)間(即,每秒15-25幀)。您可以通過Edit->Project Settings->Time來(lái)改變這個(gè)值。這樣做降低了FixedUpdate函數(shù)被調(diào)用的頻率以及物理引擎執(zhí)行碰撞檢測(cè)與剛體更新的頻率。如果您使用了較低的固定增量時(shí)間,并且在主角身上使用了剛體部件,那么您可以啟用插值辦法來(lái)平滑剛體組件。
7,減少GetComponent的調(diào)用
使用 GetComponent或內(nèi)置組件訪問器會(huì)產(chǎn)生明顯的開銷。您可以通過一次獲取組件的引用來(lái)避免開銷,并將該引用分配給一個(gè)變量(有時(shí)稱為"緩存"的引用)。例如,如果您使用如下的代碼:
function Update () { transform.Translate(0, 1, 0); }
通過下面的更改您將獲得更好的性能:
function Awake ()
{
myTransform = transform;
}
function Update ()
{
myTransform.Translate(0, 1, 0);
}
```
8,避免分配內(nèi)存
您應(yīng)該避免分配新對(duì)象,除非你真的需要,因?yàn)樗麄儾辉僭谑褂脮r(shí),會(huì)增加垃圾回收系統(tǒng)的開銷。您可以經(jīng)常重復(fù)使用數(shù)組和其他對(duì)象,而不是分配新的數(shù)組或?qū)ο蟆_@樣做好處則是盡量減少垃圾的回收工作。同時(shí),在某些可能的情況下,您也可以使用結(jié)構(gòu)(struct)來(lái)代替類(class)。這是因?yàn)?,結(jié)構(gòu)變量主要存放在棧區(qū)而非堆區(qū)。因?yàn)闂5姆峙漭^快,并且不調(diào)用垃圾回收操作,所以當(dāng)結(jié)構(gòu)變量比較小時(shí)可以提升程序的運(yùn)行性能。但是當(dāng)結(jié)構(gòu)體較大時(shí),雖然它仍可避免分配/回收的開銷,而它由于"傳值"操作也會(huì)導(dǎo)致單獨(dú)的開銷,實(shí)際上它可能比等效對(duì)象類的效率還要低。
9,使用iOS腳本調(diào)用優(yōu)化功能
UnityEngine 命名空間中的函數(shù)的大多數(shù)是在 C/c + +中實(shí)現(xiàn)的。從Mono的腳本調(diào)用 C/C++函數(shù)也存在著一定的性能開銷。您可以使用iOS腳本調(diào)用優(yōu)化功能(菜單:Edit->Project Settings->Player)讓每幀節(jié)省1-4毫秒。此設(shè)置的選項(xiàng)有:
Slow and Safe – Mono內(nèi)部默認(rèn)的處理異常的調(diào)用
Fast and Exceptions Unsupported –一個(gè)快速執(zhí)行的Mono內(nèi)部調(diào)用。不過,它并不支持異常,因此應(yīng)謹(jǐn)慎使用。它對(duì)于不需要顯式地處理異常(也不需要對(duì)異常進(jìn)行處理)的應(yīng)用程序來(lái)說(shuō),是一個(gè)理想的候選項(xiàng)。
10,
優(yōu)化垃圾回收
如上文所述,您應(yīng)該盡量避免分配操作。但是,考慮到它們是不能完全杜絕的,所以我們提供兩種方法來(lái)讓您盡量減少它們?cè)谟螒蜻\(yùn)行時(shí)的使用:
如果堆比較小,則進(jìn)行快速而頻繁的垃圾回收
這一策略比較適合運(yùn)行時(shí)間較長(zhǎng)的游戲,其中幀率是否平滑過渡是主要的考慮因素。像這樣的游戲通常會(huì)頻繁地分配小塊內(nèi)存,但這些小塊內(nèi)存只是暫時(shí)地被使用。如果在iOS系統(tǒng)上使用該策略,那么一個(gè)典型的堆大小是大約 200 KB,這樣在iPhone 3G設(shè)備上,垃圾回收操作將耗時(shí)大約 5毫秒。如果堆大小增加到1 MB時(shí),該回收操作將耗時(shí)大約 7ms。因此,在普通幀的間隔期進(jìn)行垃圾回收有時(shí)候是一個(gè)不錯(cuò)的選擇。通常,這種做法會(huì)讓回收操作執(zhí)行的更加頻繁(有些回收操作并不是嚴(yán)格必須進(jìn)行的),但它們可以快速處理并且對(duì)游戲的影響很?。?
``` if (Time.frameCount % 30 == 0
{
System.GC.Collect();
}
```
但是,您應(yīng)該小心地使用這種技術(shù),并且通過檢查Profiler來(lái)確保這種操作確實(shí)可以降低您游戲的垃圾回收時(shí)間
如果堆比較大,則進(jìn)行緩慢且不頻繁的垃圾回收
這一策略適合于那些內(nèi)存分配 (和回收)相對(duì)不頻繁,并且可以在游戲停頓期間進(jìn)行處理的游戲。如果堆足夠大,但還沒有大到被系統(tǒng)關(guān)掉的話,這種方法是比較適用的。但是,Mono運(yùn)行時(shí)會(huì)盡可能地避免堆的自動(dòng)擴(kuò)大。因此,您需要通過在啟動(dòng)過程中預(yù)分配一些空間來(lái)手動(dòng)擴(kuò)展堆(ie,你實(shí)例化一個(gè)純粹影響內(nèi)存管理器分配的"無(wú)用"對(duì)象):
```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;
}
```
游戲中的暫停是用來(lái)對(duì)堆內(nèi)存進(jìn)行回收,而一個(gè)足夠大的堆應(yīng)該不會(huì)在游戲的暫停與暫停之間被完全占滿。所以,當(dāng)這種游戲暫停發(fā)生時(shí),您可以顯式請(qǐng)求一次垃圾回收:
```System.GC.Collect();
```
另外,您應(yīng)該謹(jǐn)慎地使用這一策略并時(shí)刻關(guān)注Profiler的統(tǒng)計(jì)結(jié)果,而不是假定它已經(jīng)達(dá)到了您想要的效果。
三,模型
1,壓縮 Mesh
導(dǎo)入 3D 模型之后,在不影響顯示效果的前提下,最好打開 Mesh Compression。
Off, Low, Medium, High 這幾個(gè)選項(xiàng),可酌情選取。
2,避免大量使用 Unity 自帶的 Sphere 等內(nèi)建 Mesh
Unity 內(nèi)建的 Mesh,多邊形的數(shù)量比較大,如果物體不要求特別圓滑,可導(dǎo)入其他的簡(jiǎn)單3D模型代替。