Unity 優(yōu)化歸納

原文地址 http://www.fx114.net/qa-75-172454.aspx

使用Profiler工具分析內(nèi)存占用情況

System.ExecutableAndDlls:系統(tǒng)可執(zhí)行程序和DLL,是只讀的內(nèi)存,用來執(zhí)行所有的腳本和DLL引用。不同平臺和不同硬件得到的值會不一樣,可以通過修改Player
Setting的Stripping Level來調(diào)節(jié)大小。

Ricky:我試著修改了一下Stripping Level似乎沒什么改變,感覺雖占用內(nèi)存大但不會影響游戲運行。我們暫時忽略它吧(- -)!

GfxClientDevice:GFX(圖形加速\圖形加速器\顯卡
(GraphicsForce Express))客戶端設(shè)備。

Ricky:雖占用較大內(nèi)存,但這也是必備項,沒辦法優(yōu)化。繼續(xù)忽略吧(- -)!!

ManagedHeap.UsedSize:托管堆使用大小。

Ricky:重點監(jiān)控對象,不要讓它超過20MB,否則可能會有性能問題!

ShaderLab:Unity自帶的著色器語言工具相關(guān)資源。

Ricky:這個東西大家都比較熟悉了,忽略它吧。

SerializedFile:序列化文件,把顯示中的Prefab、Atlas和metadata等資源加載進內(nèi)存。

Ricky:重點監(jiān)控對象,這里就是你要監(jiān)控的哪些預設(shè)在序列化中在內(nèi)存中占用大小,根據(jù)需求進行優(yōu)化。

PersistentManager.Remapper:持久化數(shù)據(jù)重映射管理相關(guān)

Ricky:與持久化數(shù)據(jù)相關(guān),比如AssetBundle之類的。注意監(jiān)控相關(guān)的文件。

ManagedHeap.ReservedUnusedSize:托管堆預留不使用內(nèi)存大小,只由Mono使用。

Ricky:無法優(yōu)化。

許多貼圖采用的Format格式是ARGB
32 bit所以保真度很高但占用的內(nèi)存也很大。在不失真的前提下,適當壓縮貼圖,使用ARGB 16
bit就會減少一倍,如果繼續(xù)Android采用RGBA Compressed ETC2 8 bits(iOS采用RGBA Compressed
PVRTC 4 bits),又可以再減少一倍。把不需要透貼但有alpha通道的貼圖,全都轉(zhuǎn)換格式Android:RGB
Compressed ETC 4 bits,iOS:RGB Compressed PVRTC 4 bits。
當加載一個新的Prefab或貼圖,不及時回收,它就會永駐在內(nèi)存中,就算切換場景也不會銷毀。應(yīng)該確定物體不再使用或長時間不使用就先把物體制空(null),然后調(diào)用Resources.UnloadUnusedAssets(),才能真正釋放內(nèi)存。
有大量空白的圖集貼圖,可以用TexturePacker等工具進行優(yōu)化或考慮合并到其他圖集中。

AudioManager:音頻管理器

Ricky:隨著音頻文件的增多而增大。

AudioClip:音效及聲音文件

Ricky:重點優(yōu)化對象,播放時長較長的音樂文件需要進行壓縮成.mp3或.ogg格式,時長較短的音效文件可以使用.wav 或.aiff格式。

Cubemap:立方圖紋理

Ricky:這個一般在天空盒中比較常見,我也不知道如何優(yōu)化這個。。。

Mesh:模型網(wǎng)格

Ricky:主要檢查是否有重復的資源,還有盡量減少點面數(shù)。

Mesh:場景中使用的網(wǎng)格模型

Ricky:注意網(wǎng)格模型的點面數(shù),能合并的mesh盡量合并。

1)ManagedHeap.UsedSize: 移動游戲建議不要超過20MB.

2)SerializedFile: 通過異步加載(LoadFromCache、WWW等)的時候留下的序列化文件,可監(jiān)視是否被卸載.

3)WebStream: 通過異步WWW下載的資源文件在內(nèi)存中的解壓版本,比SerializedFile大幾倍或幾十倍,不過我們現(xiàn)在項目中展示沒有。

4)Texture2D: 重點檢查是否有重復資源和超大Memory是否需要壓縮等.

5)AnimationClip: 重點檢查是否有重復資源.

6)Mesh: 重點檢查是否有重復資源.

1.Device.Present:

1)GPU的presentdevice確實非常耗時,一般出現(xiàn)在使用了非常復雜的shader.

2)GPU運行的非???,而由于Vsync的原因,使得它需要等待較長的時間.

3)同樣是Vsync的原因,但其他線程非常耗時,所以導致該等待時間很長,比如:過量AssetBundle加載時容易出現(xiàn)該問題.

4)Shader.CreateGPUProgram:Shader在runtime階段(非預加載)會出現(xiàn)卡頓(華為K3V2芯片).

5)StackTraceUtility.PostprocessStacktrace()和StackTraceUtility.ExtractStackTrace(): 一般是由Debug.Log或類似API造成,游戲發(fā)布后需將Debug API進行屏蔽。

2.Overhead:

1)一般情況為Vsync所致.

2)通常出現(xiàn)在Android設(shè)備上.

3.GC.Collect:

原因:

1)代碼分配內(nèi)存過量(惡性的)

2)一定時間間隔由系統(tǒng)調(diào)用(良性的).

占用時間:

1)與現(xiàn)有Garbage size相關(guān)

2)與剩余內(nèi)存使用顆粒相關(guān)(比如場景物件過多,利用率低的情況下,GC釋放后需要做內(nèi)存重排)

4.GarbageCollectAssetsProfile:

1)引擎在執(zhí)行UnloadUnusedAssets操作(該操作是比較耗時的,建議在切場景的時候進行)。

2)盡可能地避免使用Unity內(nèi)建GUI,避免GUI.Repaint過渡GCAllow.

3)if(other.tag == a.tag)改為other.CompareTag(a.tag).因為other.tag為產(chǎn)生180B的GC Allow.

4)少用foreach,因為每次foreach為產(chǎn)生一個enumerator(約16B的內(nèi)存分配),盡量改為for.

5)Lambda表達式,使用不當會產(chǎn)生內(nèi)存泄漏.

5.盡量少用LINQ:

1)部分功能無法在某些平臺使用.

2)會分配大量GC Allow.

6.控制StartCoroutine的次數(shù):

1)開啟一個Coroutine(協(xié)程),至少分配37B的內(nèi)存.

2)Coroutine類的實例 -> 21B.

3)Enumerator -> 16B.

7.使用StringBuilder替代字符串直接連接.

8.緩存組件:

1)每次GetComponent均會分配一定的GC Allow.

2)每次Object.name都會分配39B的堆內(nèi)存.

.框架設(shè)計層面。

一個相對中大型的游戲,系統(tǒng)非常的多。這時候合理的適時的釋放內(nèi)存有助于游戲的正常體驗,甚至可以防止內(nèi)存快速到達峰值,導致設(shè)備Crash。

目前主流平臺機型可用內(nèi)存:

Android平臺:在客戶端最低配置以上,均需滿足以下內(nèi)存消耗指標(PSS):

1)內(nèi)存1G以下機型:最高PSS<=150MB

2)內(nèi)存2G的機型:最高PSS<=200MB

iOS平臺:在iPhone4S下運行,消耗內(nèi)存(real mem)不大于150MB

1.場景切換時避開峰值。

當前一個場景還未釋放的時候,切換到新的場景。這時候由于兩個內(nèi)存疊加很容易達到內(nèi)存峰值。解決方案是,在屏幕中間遮蓋一個Loading場景。在舊的釋放完,并且新的初始化結(jié)束后,隱藏Loading場景,使之有效的避開內(nèi)存大量疊加超過峰值。

2.GUI模塊加入生命周期管理。

主角、強化、技能、商城、進化、背包、任務(wù)等等。通常一個游戲都少不了這些系統(tǒng)。但要是全部都打開,或者這個時候再點世界地圖,外加一些邏輯數(shù)據(jù)內(nèi)存的占用等等。你會發(fā)現(xiàn),內(nèi)存也很快就達到峰值。

這時候有效的管理系統(tǒng)模塊生命周期就非常有必要。首先將模塊進行劃分:

1)經(jīng)常打開 Cache_10;

2)偶爾打開 Cache_5;

3)只打開一次 Cache_0。

創(chuàng)建一個ModuleMananger
類,內(nèi)部Render方法每分鐘輪詢一次。如果是“Cache_0”這個類型,一關(guān)閉就直接Destroy釋放內(nèi)存;“Cache_10”這個類型為10分鐘后自動釋放內(nèi)存;"
Cache_5"這種類型為5分鐘后自動釋放內(nèi)存。每次打開模塊,該模塊就會重新計時。這樣就可以有效合理的分配內(nèi)存。

1、 由于實時對戰(zhàn)游戲的數(shù)據(jù)包數(shù)量巨大,早期版本的幀同步策略會導致比較明顯的卡頓,通過進行數(shù)據(jù)包的合并與優(yōu)化逐漸解決了卡頓問題;

2、 頻繁創(chuàng)建和銷毀的小兵對象讓CPU爆表了,大量的小兵如果采用實時內(nèi)存的分配和回收,會產(chǎn)生大量的內(nèi)存碎片和系統(tǒng)開銷,解決方法之一就是采用高效的對象池進行優(yōu)化,對每個內(nèi)存對象的狀態(tài)進行操作即可;

3、 性能分析過程中,發(fā)現(xiàn)單人同屏和多人同屏時的開銷都很大,通過視野裁剪技術(shù),使得玩家視野外的不必要的特效和渲染可以全部關(guān)閉,極大降低了CPU、GPU和內(nèi)存的開銷;

4、 在高中低三檔機型上玩游戲時,分別加載不同層次的特效包,這也有助于降低CPU和內(nèi)存的開銷;性能分析過程中發(fā)現(xiàn)副本內(nèi)wwise音頻組件占了30%的CPU時間,果斷拋棄之,采用Unity自帶音頻功能,優(yōu)化很明顯;

5、 游戲內(nèi)界面采用了UGUI的方式實現(xiàn),但大量的實時UI變化使得副本內(nèi)每幀會有230以上的drawcall,導致中低端機型感受到明顯卡頓,最終采用UGUI+自研究UI的組合拳,重寫了一套緊密結(jié)合游戲自身特性的UI來實現(xiàn)戰(zhàn)斗血條和浮動文字的效果。

6、 資源使用總量是否在合理范圍之內(nèi)。

7、 一個場景內(nèi)的資源重復率。

8、 資源對象拷貝的數(shù)量是否合理。

9、 場景切換時保留的資源詳情。

10、 網(wǎng)格、紋理、音頻、動畫、GameObject等資源是否超標。

11、 貼圖:

12、 l 控制貼圖大小,盡量不要超過 1024x1024;

13、 l 盡量使用2的n次冪大小的貼圖,否則GfxDriver里會有2份貼圖;

14、 l 盡量使用壓縮格式減小貼圖大??;

15、 l 若干種貼圖合并技術(shù);

16、 l 去除多余的alpha通道;

17、 l 不同設(shè)備使用不同的紋理貼圖,分層顯示;

18、

19、 模型:

20、 l 盡量控制模型的面數(shù),小于1500會比較合適;

21、 l 不同設(shè)備使用不同的模型面數(shù);

22、 l 盡量保持在30根骨骼內(nèi);

23、 l 一個網(wǎng)格不要超過3個material;

24、 動畫:

25、 l N種動畫壓縮方法;

26、 l 盡量減少骨骼數(shù)量;

27、 聲音:

28、 l 采用壓縮MP3 和 wav;

29、 資源方面的優(yōu)化:

30、 l 使用 Resource.Load 方法在需要的時候再讀取資源;

31、 l 各種資源在使用完成后,盡快用Resource.UnloadAsset和UnloadUnusedAsset卸載掉;

32、 l 靈活運用AssetBundle的Load和Unload方法動態(tài)加載資源,避免主要場景內(nèi)的初始化內(nèi)存占用過高;(實現(xiàn)起來真的很難…)

33、 l 采用www加載了AssetBundle后,要用www.Dispose 及時釋放;

34、 l 在關(guān)卡內(nèi)謹慎使用DontDestroyOnLoad,被標注的資源會常駐內(nèi)存;

35、 代碼的優(yōu)化:

36、 l 盡量避免代碼中的任何字符串連接,因為這會給GC帶來太多垃圾;

37、 l 用簡單的“for”循環(huán)代替“foreach”循環(huán);

38、 l 為所有游戲內(nèi)的動態(tài)物體使用內(nèi)存對象池,可以減少系統(tǒng)開銷和內(nèi)存碎片,復用對象實例,構(gòu)建自己的內(nèi)存管理模式,減少Instantiate和Destory;

39、 l 盡量不使用LINQ命令,因為它們一般會分配中間緩器,而這很容易生成垃圾內(nèi)存;

40、 l 將引用本地緩存到元件中會減少每次在一個游戲?qū)ο笾惺褂?“GetComponent” 獲取一個元件引用的需求;

41、 l 減少角色控制器移動命令的調(diào)用。移動角色控制器會同步發(fā)生,每次調(diào)用都會耗損較大的性能;

42、 l 最小化碰撞檢測請求(例如raycasts和sphere checks),盡量從每次檢查中獲得更多信息;

43、 l AI邏輯通常會生成大量物理查詢,建議讓AI更新循環(huán)設(shè)置低于圖像更新循環(huán),以減少CPU負荷;

44、 l 要盡量減少Unity回調(diào)函數(shù),哪怕是空函數(shù)也不要留著;(例如空的Update、FixedUpdate函數(shù))

45、 l 盡量少使用FindObjectsOfType函數(shù),這個函數(shù)非常慢,盡量少用且一定不要在Update里調(diào)用;

46、 l 千萬一定要控制mono堆內(nèi)存的大??;

47、

48、 unity3D 對于移動平臺的支持無可厚非,但是也有時候用Unity3D 開發(fā)出來的應(yīng)用、游戲在移動終端上的運行有著明顯的效率問題,比如卡、畫質(zhì)等各種問題。自己在做游戲開發(fā)的時候偶有所得。對于主要影響性能的因素做個總結(jié)。

49、

50、 主要因素有:

51、 1. Savedby batching 值過大 ---- > 這個值主要是針對Mesh的批處理,這個值越高,應(yīng)用就越卡

52、 2. Drawcall值過大 ---- > Drawcall 值過大,所需要的 GPU 的處理性能較高,從而導致CPU的計算時間過長,于是就卡了

53、 3. 點、面過多 ----> 點、面過多,GPU 根據(jù)不同面的效果展開計算,并且CPU計算的數(shù)據(jù)也多,所以效果出來了,但是卡巴斯基

54、 由于 Saved by batching 和 Drawcall 值過大所引起的卡的問題我所做的優(yōu)化方式有:

55、 1. 對于模型 :Mesh 合并,有個不錯的插件(DrawCallMinimizer ---> 直接上AssetStore 下載即可,免費的,而且有文檔,很容易上手)

56、 2. 對于UI : 盡量避免使用Unity3D自帶的 GUI 換用 NGUI或者EZGUI;因為這兩個UI插件對于UI中的圖片處理是將UI圖片放置在一個 Atlas中,一個 Atlas 對應(yīng)一個Drawcall

57、 3. 對于燈光: 可以使用 Unity3D 自帶的 Lightmapping插件來烘焙場景中的燈光效果到物體材質(zhì)上

58、 4. 對于場景: 可以使用 Unity3D 自帶的 OcclusionCulling 插件把靜止不動的場景元素烘焙出來

59、 4. 對于特效:盡量把材質(zhì)紋理合并

60、 對于Unity3D 在移動終端上支持的Drawcall
數(shù)到底多少,主要是跟機子性能有關(guān)的,當然也不是說值小性能就一定沒問題(本人親測,也有17就卡的,主要是模型材質(zhì)紋理過大所引起的),目前我做的是70左右的,還OK,挺正常的

61、

62、 由于點、面過多所導致的性能問題,最好用簡模,用四面體來做復雜的模型,但是面、點也別太多,至于Unity3D 到底支持多少點、面的說法各異,我也搞不懂,總之少些肯定OK

63、

64、

65、

66、 檢測方式:

67、 一,Unity3D 渲染統(tǒng)計窗口

68、 Game視窗的Stats去查看渲染統(tǒng)計的信息:

69、 1、FPS

70、 fps其實就是 framesper second,也就是每一秒游戲執(zhí)行的幀數(shù),這個數(shù)值越小,說明游戲越卡。

71、

72、 2、Draw calls

73、 batching之后渲染mesh的數(shù)量,和當前渲染到的網(wǎng)格的材質(zhì)球數(shù)量有關(guān)。

74、

75、 3、Saved by batching

76、 渲染的批處理數(shù)量,這是引擎將多個對象的繪制進行合并從而減少GPU的開銷;

77、 很多GUI插件的一個好處就是合并多個對象的渲染,從而降低DrawCalls ,保證游戲幀數(shù)。

78、

79、 4、Tris 當前繪制的三角面數(shù)

80、

81、 5、Verts 當前繪制的頂點數(shù)

82、

83、 6、Used Textures 當前幀用于渲染的圖片占用內(nèi)存大小

84、

85、 7、Render Textures 渲染的圖片占用內(nèi)存大小,也就是當然渲染的物體的材質(zhì)上的紋理總內(nèi)存占用

86、

87、 8、VRAM usage 顯存的使用情況,VRAM總大小取決于你的顯卡的顯存

88、

89、 9、VBO Total 渲染過程中上載到圖形卡的網(wǎng)格的數(shù)量,這里注意一點就是縮放的物體可能需要額外的開銷。

90、

91、 10、VisibleSkinned Meshes 蒙皮網(wǎng)格的渲染數(shù)量

92、

93、 11、Animations 播放動畫的數(shù)量

94、 注意事項:

95、 1,運行時盡量減少 Tris 和 Draw Calls

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

97、 一般來說,要做到:

98、 Tris 保持在 7.5k 以下,有待考證。

99、 Draw Calls 保持在 20 以下,有待考證。

100、 2,F(xiàn)PS,每一秒游戲執(zhí)行的幀數(shù),這個數(shù)值越小,說明游戲越卡。

101、 3,Render Textures 渲染的圖片占用內(nèi)存大小。

102、 4,VRAM usage 顯存的使用情況,VRAM總大小取決于你的顯卡的顯存。

103、

104、 二,代碼優(yōu)化

105、 1. 盡量避免每幀處理

106、 比如:

107、 function Update() {DoSomeThing(); }

108、 可改為每5幀處理一次:

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

110、 2. 定時重復處理用InvokeRepeating 函數(shù)實現(xiàn)

111、 比如,啟動0.5秒后每隔1秒執(zhí)行一次 DoSomeThing 函數(shù):

112、

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

114、

115、 3. 優(yōu)化 Update,FixedUpdate, LateUpdate 等每幀處理的函數(shù)

116、 函數(shù)里面的變量盡量在頭部聲明。

117、 比如:

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

119、 可改為

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

121、

122、 4. 主動回收垃圾

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

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

125、

126、 5. 優(yōu)化數(shù)學計算

127、 比如,如果可以避免使用浮點型(float),盡量使用整形(int),盡量少用復雜的數(shù)學函數(shù)比如 Sin 和 Cos 等等

128、

129、 6,減少固定增量時間

130、 將固定增量時間值設(shè)定在0.04-0.067區(qū)間(即,每秒15-25幀)。您可以通過Edit->Project

Settings->Time來改變這個值。這樣做降低了FixedUpdate函數(shù)被調(diào)用的頻率以及物理引擎執(zhí)行碰撞檢測與剛體更新的頻率。如果您使用了較低的固定增量時間,并且在主角身上使用了剛體部件,那么您可以啟用插值辦法來平滑剛體組件。

131、 7,減少GetComponent的調(diào)用

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

133、 function Update () {

134、 transform.Translate(0, 1, 0);

135、

136、 }

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

138、

139、 var myTransform : Transform;

140、 function Awake () {

141、 myTransform = transform;

142、 }

143、 function Update () {

144、 myTransform.Translate(0, 1, 0);

145、 }

146、

147、 8,避免分配內(nèi)存

148、 您應(yīng)該避免分配新對象,除非你真的需要,因為他們不再在使用時,會增加垃圾回收系統(tǒng)的開銷。您可以經(jīng)常重復使用數(shù)組和其他對象,而不是分配新的數(shù)組或?qū)ο蟆_@樣做好處則是盡量減少垃圾的回收工作。同時,在某些可能的情況下,您也可以使用結(jié)構(gòu)(struct)來代替類(class)。這是因為,結(jié)構(gòu)變量主要存放在棧區(qū)而非堆區(qū)。因為棧的分配較快,并且不調(diào)用垃圾回收操作,所以當結(jié)構(gòu)變量比較小時可以提升程序的運行性能。但是當結(jié)構(gòu)體較大時,雖然它仍可避免分配/回收的開銷,而它由于"傳值"操作也會導致單獨的開銷,實際上它可能比等效對象類的效率還要低。

149、

150、 9,使用iOS腳本調(diào)用優(yōu)化功能

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

152、 Slow and Safe – Mono內(nèi)部默認的處理異常的調(diào)用

153、

154、 Fast and Exceptions Unsupported–一個快速執(zhí)行的Mono內(nèi)部調(diào)用。不過,它并不支持異常,因此應(yīng)謹慎使用。它對于不需要顯式地處理異常(也不需要對異常進行處理)的應(yīng)用程序來說,是一個理想的候選項。

155、

156、 10,

157、 優(yōu)化垃圾回收

158、

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

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

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

162、 if (Time.frameCount % 30 == 0)

163、 {

164、 System.GC.Collect();

165、 }

166、

167、 但是,您應(yīng)該小心地使用這種技術(shù),并且通過檢查Profiler來確保這種操作確實可以降低您游戲的垃圾回收時間

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

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

170、

171、 function Start() {

172、

173、 var tmp = newSystem.Object[1024];

174、

175、 // make allocations in smallerblocks to avoid them to
be treated in a special way, which is designed forlarge blocks

176、

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

178、

179、 tmp[i] = new byte[1024];

180、

181、 // release reference

182、

183、 tmp = null;

184、

185、 }

186、

187、 游戲中的暫停是用來對堆內(nèi)存進行回收,而一個足夠大的堆應(yīng)該不會在游戲的暫停與暫停之間被完全占滿。所以,當這種游戲暫停發(fā)生時,您可以顯式請求一次垃圾回收:

188、

189、 System.GC.Collect();

190、

191、 另外,您應(yīng)該謹慎地使用這一策略并時刻關(guān)注Profiler的統(tǒng)計結(jié)果,而不是假定它已經(jīng)達到了您想要的效果。

192、

193、 三,模型

194、 1,壓縮 Mesh

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

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

197、 2,避免大量使用 Unity 自帶的 Sphere 等內(nèi)建 Mesh

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

199、

200、 1不是每個主流手機都支持的技術(shù)(就是如果可以不用就不用或有備選方案)

201、 屏幕特效

202、 動態(tài)的pixel光照計算(如法線)

203、 實時的陰影

204、

205、 2優(yōu)化建議

206、 2.1渲染

207、 1.不使用或少使用動態(tài)光照,使用light mapping和light probes(光照探頭)

208、 2.不使用法線貼圖(或者只在主角身上使用),靜態(tài)物體盡量將法線渲染到貼圖

209、 3.不適用稠密的粒子,盡量使用UV動畫

210、 4.不使用fog,使用漸變的面片(參考shadowgun)

211、 5.不要使用alpha–test(如那些cutout shader),使用alpha-blend代替

212、 6.使用盡量少的material,使用盡量少的pass和render次數(shù),如反射、陰影這些操作

213、 7.如有必要,使用Per-LayerCull Distances,Camera.layerCullDistances

214、 8.只使用mobile組里面的那些預置shader

215、 9.使用occlusionculling

216、 11.遠處的物體繪制在skybox上

217、 12.使用drawcallbatching:

218、 對于相鄰動態(tài)物體:如果使用相同的shader,將texture合并

219、 對于靜態(tài)物體,batching要求很高,詳見Unity Manual>Advanced>Optimizing
Graphics Performance>Draw
Call Batching

220、

221、 規(guī)格上限

222、 1. 每個模型只使用一個skinnedmesh renderer

223、 2. 每個mesh不要超過3個material

224、 3. 骨骼數(shù)量不要超過30

225、 4. 面數(shù)在1500以內(nèi)將得到好的效率

226、 2.2物理

227、 1.真實的物理(剛體)很消耗,不要輕易使用,盡量使用自己的代碼模仿假的物理

228、 2.對于投射物不要使用真實物理的碰撞和剛體,用自己的代碼處理

229、 3.不要使用meshcollider

230、 4.在edit->projectsetting->time中調(diào)大FixedTimestep(真實物理的幀率)來減少cpu損耗

231、 2.3腳本編寫

232、 1.盡量不要動態(tài)的instantiate和destroyobject,使用object pool

233、 2.盡量不要再update函數(shù)中做復雜計算,如有需要,可以隔N幀計算一次

234、 3.不要動態(tài)的產(chǎn)生字符串,如Debug.Log("boo"+ "hoo"),盡量預先創(chuàng)建好這些字符串資源

235、 4.cache一些東西,在update里面盡量避免search,如GameObject.FindWithTag("")、GetComponent這樣的調(diào)用,可以在start中預先存起來

236、 5.盡量減少函數(shù)調(diào)用棧,用x= (x > 0 ? x : -x);代替x = Mathf.Abs(x)

237、 6.下面的代碼是幾個gc“噩夢”

238、 String的相加操作,會頻繁申請內(nèi)存并釋放,導致gc頻繁,使用System.Text.StringBuilder代替

239、 functionConcatExample(intArray: int[]) {

240、 varline = intArray[0].ToString();

241、

242、 for(i = 1; i < intArray.Length; i++) {

243、 line+= ", " + intArray[i].ToString();

244、 }

245、

246、 returnline;

247、 }

248、 在函數(shù)中動態(tài)new array,最好將一個array、傳進函數(shù)里修改

249、 functionRandomList(numElements: int) {

250、 varresult = new float[numElements];

251、

252、 for(i = 0; i < numElements; i++) {

253、 result[i]= Random.value;

254、 }

255、

256、 returnresult;

257、 }

258、

259、 2.4 shader編寫

260、 1.數(shù)據(jù)類型

261、 fixed / lowp -for colors, lighting information and normals,

262、 half / mediump -for texture UV coordinates,

263、 float / highp -avoid in pixel shaders, fine to use in vertex shader for position calculations.

264、 2.少使用的函數(shù):pow,sin,cos等

265、 2.4 GUI

266、 1.不要使用內(nèi)置的onGUii函數(shù)處理gui,使用其他方案,如NGUI

267、

268、 3.格式

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

270、 最簡單的優(yōu)化建議:

1.PC平臺的話保持場景中顯示的頂點數(shù)少于200K~3M,移動設(shè)備的話少于10W,一切取決于你的目標GPU與CPU。
2.如果你用U3D自帶的SHADER,在表現(xiàn)不差的情況下選擇Mobile或Unlit目錄下的。它們更高效。
3.盡可能共用材質(zhì)。
4.將不需要移動的物體設(shè)為Static,讓引擎可以進行其批處理。
5.盡可能不用燈光。
6.動態(tài)燈光更加不要了。
7.嘗試用壓縮貼圖格式,或用16位代替32位。
8.如果不需要別用霧效(fog)
9.嘗試用OcclusionCulling,在房間過道多遮擋物體多的場景非常有用。若不當反而會增加負擔。
10.用天空盒去“褪去”遠處的物體。
11.shader中用貼圖混合的方式去代替多重通道計算。
12.shader中注意float/half/fixed的使用。
13.shader中不要用復雜的計算pow,sin,cos,tan,log等。
14.shader中越少Fragment越好。
15.注意是否有多余的動畫腳本,模型自動導入到U3D會有動畫腳本,大量的話會嚴重影響消耗CPU計算。
16.注意碰撞體的碰撞層,不必要的碰撞檢測請舍去。

1.為什么需要針對CPU(中央處理器)與GPU(圖形處理器)優(yōu)化?

CPU和GPU都有各自的計算和傳輸瓶頸,不同的CPU或GPU他們的性能都不一樣,所以你的游戲需要為你目標用戶的CPU與GPU能力進行針對開發(fā)。

2.CPU與GPU的限制

GPU一般具有填充率(Fillrate)和內(nèi)存帶寬(Memory Bandwidth)的限制,如果你的游戲在低質(zhì)量表現(xiàn)的情況下會快很多,那么,你很可能需要限制你在GPU的填充率。

CPU一般被所需要渲染物體的個數(shù)限制,CPU給GPU發(fā)送渲染物體命令叫做DrawCalls。一般來說DrawCalls數(shù)量是需要控制的,在能表現(xiàn)效果的前提下越少越好。通常來說,電腦平臺上DrawCalls幾千個之內(nèi),移動平臺上DrawCalls幾百個之內(nèi)。這樣就差不多了。當然以上并不是絕對的,僅作一個參考。

往往渲染(Rendering)并不是一個問題,無論是在GPU和CPU上。很可能是你的腳本代碼效率的問題,用Profiler查看下。

關(guān)于Profiler介紹:http://docs.unity3d.com/Documentation/Manual/Profiler.html

需要注意的是:
在GPU中顯示的RenderTexture.SetActive()占用率很高,是因為你同時打開了編輯窗口的原因,而不是U3D的BUG。

3.關(guān)于頂點數(shù)量和頂點計算

CPU和GPU對頂點的計算處理都很多。GPU中渲染的頂點數(shù)取決于GPU性能和SHADER的復雜程度,一般來說,每幀之內(nèi),在PC上幾百萬頂點內(nèi),在移動平臺上不超過10萬頂點。

CPU中的計算主要是在蒙皮骨骼計算,布料模擬,頂點動畫,粒子模擬等。GPU則在各種頂點變換、光照、貼圖混合等。

【個人認為,具體還是看各位的項目需求,假設(shè)你項目的是3d游戲。你游戲需要兼容低配置的硬件、流暢運行、控制硬件發(fā)熱的話,還要達到一定效果(LIGHTMAP+霧效),那么頂點數(shù)必定不能高。此時同屏2W頂點我認為是個比較合適的數(shù)目,DRAWCALL最好低于70。另,控制發(fā)熱請控制最高上限的幀率,流暢的話,幀率其實不需要太高的?!?/p>

4.針對CPU的優(yōu)化——減少DRAW CALL 的數(shù)量

為了渲染物體到顯示器上,CPU需要做一些工作,如區(qū)分哪個東西需要渲染、區(qū)分開物體是否受光照影響、使用哪個SHADER并且為SHADER傳參、發(fā)送繪圖命令告訴顯示驅(qū)動,然后發(fā)送命令告訴顯卡刪除等這些。

假設(shè)你有一個上千三角面的模型卻用上千個三角型模型來代替,在GPU上花費是差不多的,但是在CPU上則是極其不一樣,消耗會大很多很多。為了讓CPU更少的工作,需要減少可見物的數(shù)目:

a.合并相近的模型,手動在模型編輯器中合并或者使用UNITY的Draw call批處理達到相同效果(Draw call batching)。具體方法和注意事項查看以下鏈接:

Draw call batching :http://docs.unity3d.com/Documentation/Manual/DrawCallBatching.html

b.在項目中使用更少的材質(zhì)(material),將幾個分開的貼圖合成一個較大的圖集等方式處理。

如果你需要通過腳本來控制單個材質(zhì)屬性,需要注意改變Renderer.material將會造成一份材質(zhì)的拷貝。因此,你應(yīng)該使用Renderer.sharedMaterial來保證材質(zhì)的共享狀態(tài)。

有一個合并模型材質(zhì)不錯的插件叫Mesh Baker,大家可以考慮試下。

c.盡量少用一些渲染步驟,例如reflections,shadows,per-pixel light 等。

d.Draw call batching的合并物體,會使每個物體(合并后的物體)至少有幾百個三角面。

假設(shè)合并的兩個物體(手動合并)但不共享材質(zhì),不會有性能表現(xiàn)上的提升。多材質(zhì)的物體相當于兩個物體不用一個貼圖。所以,為了提升CPU的性能,你應(yīng)該確保這些物體使用同樣的貼圖。

另外,用燈光將會取消(break)引擎的DRAW CALL BATCH,至于為什么,查看以下:

Forward Rendering Path Details:
http://docs.unity3d.com/Documentation/Components/RenderTech-ForwardRendering.html

e.使用相關(guān)剔除數(shù)量直接減少Draw Call數(shù)量,下文有相關(guān)提及。

5.優(yōu)化幾何模型

最基本的兩個優(yōu)化準則:
a.不要有不必要的三角面。
b.UV貼圖中的接縫和硬邊越少越好。

需要注意的是,圖形硬件需要處理頂點數(shù)并跟硬件報告說的并不一樣。不是硬件說能渲染幾個點就是幾個點。模型處理應(yīng)用通展示的是幾何頂點數(shù)量。例如,一個由一些不同頂點構(gòu)成的模型。在顯卡中,一些集合頂點將會被分離(split)成兩個或者更多邏輯頂點用作渲染。如果有法線、UV坐標、頂點色的話,這個頂點必須會被分離。所以在游戲中處理的實際數(shù)量顯然要多很多。

6.關(guān)于光照

若不用光肯定是最快的。移動端優(yōu)化可以采用用光照貼圖(Lightmapping)去烘培一個靜態(tài)的貼圖,以代替每次的光照計算,在U3D中只需要非常短的時間則能生成。這個方法能大大提高效率,而且有著更好的表現(xiàn)效果(平滑過渡處理,還有附加陰影等)。

在移動設(shè)備上和低端電腦上盡量不要在場景中用真光,用光照貼圖。這個方法大大節(jié)省了CPU和GPU的計算,CPU得到了更少的DRAWCALL,GPU則需要更少頂點處理和像素柵格化。

Lightmapping : http://docs.unity3d.com/Documentation/Manual/Lightmapping.html

7.對GPU的優(yōu)化——圖片壓縮和多重紋理格式

Compressed Textures(圖片壓縮):

http://docs.unity3d.com/Documentation/Components/class-Texture2D.html

圖片壓縮將降低你的圖片大?。ǜ斓丶虞d更小的內(nèi)存跨度(footprint)),而且大大提高渲染表現(xiàn)。壓縮貼圖比起未壓縮的32位RGBA貼圖占用內(nèi)存帶寬少得多。

之前U3D會議還聽說過一個優(yōu)化,貼圖盡量都用一個大小的格式(512 * 512 , 1024 * 1024),這樣在內(nèi)存之中能得到更好的排序,而不會有內(nèi)存之間空隙。這個是否真假沒得到過測試。

MIPMAps(多重紋理格式):

http://docs.unity3d.com/Documentation/Components/class-Texture2D.html

跟網(wǎng)頁上的略縮圖原理一樣,在3D游戲中我們?yōu)橛螒虻馁N圖生成多重紋理貼圖,遠處顯示較小的物體用小的貼圖,顯示比較大的物體用精細的貼圖。這樣能更加有效的減少傳輸給GPU中的數(shù)據(jù)。

8.LOD 、 Per-Layer Cull Distances 、 Occlusion Culling

LOD (Level Of Detail) 是很常用的3D游戲技術(shù)了,其功能理解起來則是相當于多重紋理貼圖。在以在屏幕中顯示模型大小的比例來判斷使用高或低層次的模型來減少對GPU的傳輸數(shù)據(jù),和減少GPU所需要的頂點計算。

攝像機分層距離剔除(Per-Layer Cull Distances):為小物體標識層次,然后根據(jù)其距離主攝像機的距離判斷是否需要顯示。

遮擋剔除(Occlusion Culling)其實就是當某個物體在攝像機前被另外一個物體完全擋住的情況,擋住就不發(fā)送給GPU渲染,從而直接降低DRAW CALL。不過有些時候在CPU中計算其是否被擋住則會很耗計算,反而得不償失。

以下是這幾個優(yōu)化技術(shù)的相關(guān)使用和介紹:

Level Of Detail :
http://docs.unity3d.com/Documentation/Manual/LevelOfDetail.html

Per-Layer Cull Distances :
http://docs.unity3d.com/Documentation/ScriptReference/Camera-layerCullDistances.html

Occlusion Culling :
http://docs.unity3d.com/Documentation/Manual/OcclusionCulling.html

9.關(guān)于Realtime Shadows(實時陰影)

實時陰影技術(shù)非常棒,但消耗大量計算。為GPU和CPU都帶來了昂貴的負擔,細節(jié)的話參考下面:

http://docs.unity3d.com/Documentation/Manual/Shadows.html

10.對GPU優(yōu)化:采用高效的shader

a.需要注意的是有些(built-in)Shader是有mobile版本的,這些大大提高了頂點處理的性能。當然也會有一些限制。

b.自己寫的shader請注意復雜操作符計算,類似pow,exp,log,cos,sin,tan等都是很耗時的計算,最多只用一次在每個像素點的計算。不推薦你自己寫normalize,dot,inversesqart操作符,內(nèi)置的肯定比你寫的好。

c.需要警醒的是alpha test,這個非常耗時。

d.浮點類型運算:精度越低的浮點計算越快。

在CG/HLSL中--

float :32位浮點格式,適合頂點變換運算,但比較慢。
half:16位浮點格式,適合貼圖和UV坐標計算,是highp類型計算的兩倍。
fixed: 10位浮點格式,適合顏色,光照,和其他。是highp格式計算的四倍。

寫Shader優(yōu)化的小提示:
http://docs.unity3d.com/Documentation/Components/SL-ShaderPerformance.html

11.另外的相關(guān)優(yōu)化:

a.對Draw Call Batching的優(yōu)化
http://docs.unity3d.com/Documentation/Manual/DrawCallBatching.html

b.對Rendering Statistics Window的說明和提示:
http://docs.unity3d.com/Documentation/Manual/RenderingStatistics.html

c.角色模型的優(yōu)化建議
用單個蒙皮渲染、盡量少用材質(zhì)、少用骨骼節(jié)點、移動設(shè)備上角色多邊形保持在3001500內(nèi)(當然還要看具體的需求)、PC平臺上15004000內(nèi)(當然還要看具體的需求)。

http://docs.unity3d.com/Documentation/Manual/ModelingOptimizedCharacters.html

渲染順序

U3D的渲染是有順序的,U3D的渲染順序是由我們控制的,控制好U3D的渲染順序,你才能控制好DrawCall

一個DrawCall,表示U3D使用這個材質(zhì)/紋理,來進行一次渲染,那么這次渲染假設(shè)有3個對象,那么當3個對象都使用這一個材質(zhì)/紋理的
時候,就會產(chǎn)生一次DrawCall,可以理解為一次將紋理輸送到屏幕上的過程,(實際上引擎大多會使用如雙緩沖,緩存這類的手段來優(yōu)化這個過程,但在這
里我們只需要這樣子認識就可以了),假設(shè)3個對象使用不同的材質(zhì)/紋理,那么無疑會產(chǎn)生3個DrawCall

接下來我們的3個對象使用2個材質(zhì),A和B使用材質(zhì)1,C使用材質(zhì)2,這時候來看,應(yīng)該是有2個DrawCall,或者3個DrawCall。
應(yīng)該是2個DrawCall啊,為什么會有3個DrawCall???而且是有時候2個,有時候3個。我們按照上面的DrawCall分析流程來分析一
下:

1.渲染A,使用材質(zhì)1
2.渲染B,使用材質(zhì)1
3.渲染C,使用材質(zhì)2

在這種情況下是2個DrawCall,在下面這種情況下,則是3個DrawCall

1.渲染A,使用材質(zhì)1
2.渲染C,使用材質(zhì)2
3.渲染B,使用材質(zhì)1

因為我們沒有控制好渲染順序(或者說沒有去特意控制),所以導致了額外的DrawCall,因為A和B不是一次性渲染完的,而是被C打斷了,所以導致材質(zhì)1被分為兩次渲染

那么是什么在控制這個渲染順序呢?首先在多個相機的情況下,U3D會根據(jù)相機的深度順序進行渲染,在每個相機中,它會根據(jù)你距離相機的距離,由遠到近進行渲染,在UI相機中,還會根據(jù)你UI對象的深度進行渲染

那么我們要做的就是,對要渲染的對象進行一次規(guī)劃,正確地排列好它們,規(guī)則是,按照Z軸或者深度,對空間進行劃分,然后確定好每個對象的Z軸和深度,讓使用同一個材質(zhì)的東西,盡量保持在這個空間內(nèi),不要讓其他材質(zhì)的對象進入這個空間,否則就會打斷這個空間的渲染順序

在這個基礎(chǔ)上,更細的規(guī)則有:

場景中的東西,我們使用Z軸來進行空間的劃分,例如背景層,特效層1,人物層,特效層2
NGUI中的東西,我們統(tǒng)一使用Depth來進行空間的劃分
人物模型,當人物模型只是用一個材質(zhì),DrawCall只有1,但是用了2個以上的材質(zhì),DrawCall就會暴增(或許對材質(zhì)的RenderQueue

進行規(guī)劃也可以使DrawCall只有2個,但這個要拆分好才行),3D人物處于復雜3D場景中的時候,我們的空間規(guī)則難免被破壞,這只能在設(shè)計的時候盡
量去避免這種情況了
使用了多個材質(zhì)的特效,在動畫的過程中,往往會引起DrawCall的波動,在視覺效果可以接受的范圍內(nèi),可以將特效也進行空間劃分,假設(shè)這個特效是2D顯示,那么可以使用Z軸來劃分空間

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容