Unity 優化的一些建議

原文標題:一些Unity 優化建議 整理(爲自己)
PS:之所以貼到我自己的博客是因爲我看到的那個網站很多黃色廣告,攔截都沒攔住,在公司打開這樣的網頁影響不好,哈!都懂的。那裏也不一定是出處,所以就沒鏈咯!!!在此感謝原創的付出與分享。

使用Profiler工具分析內存佔用情況
  • System.ExecutableAndDlls:系統可執行程序和DLL,是隻讀的內存,用來執行所有的腳本和DLL引用。不同平臺和不同硬件得到的值會不一樣,可以通過修改Player Setting的Stripping Level來調節大小。

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

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

Ricky:雖佔用較大內存,但這也是必備項,沒辦法優化。繼續忽略吧(- -)!!

  • ManagedHeap.UsedSize:託管堆使用大小。

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

  • ShaderLab:Unity自帶的着色器語言工具相關資源。

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

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

Ricky:重點監控對象,這裏就是你要監控的哪些預設在序列化中在內存中佔用大小,根據需求進行優化。

  • PersistentManager.Remapper:持久化數據重映射管理相關

Ricky:與持久化數據相關,比如AssetBundle之類的。注意監控相關的文件。

  • ManagedHeap.ReservedUnusedSize:託管堆預留不使用內存大小,只由Mono使用。

Ricky:無法優化。

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

  • AudioManager:音頻管理器

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

  • AudioClip:音效及聲音文件

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

  • Cubemap:立方圖紋理

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

  • Mesh:模型網格

Ricky:主要檢查是否有重複的資源,還有儘量減少點面數。

  • Mesh:場景中使用的網格模型

Ricky:注意網格模型的點面數,能合併的mesh儘量合併。

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

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

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

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

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

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

1.Device.Present:

1)GPU的presentdevice確實非常耗時,一般出現在使用了非常複雜的shader.

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

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

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

5)StackTraceUtility.PostprocessStacktrace()和StackTraceUtility.ExtractStackTrace(): 一般是由Debug.Log或類似API造成,遊戲發佈後需將Debug API進行屏蔽。

2.Overhead:

1)一般情況爲Vsync所致.

2)通常出現在Android設備上.

3.GC.Collect:

原因:

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

2)一定時間間隔由系統調用(良性的).

佔用時間:

1)與現有Garbage size相關

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

4.GarbageCollectAssetsProfile:

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

2)儘可能地避免使用Unity內建GUI,避免GUI.Repaint過渡GCAllow.

3)if(other.tag == a.tag)改爲other.CompareTag(a.tag).因爲other.tag爲產生180B的GC Allow.

4)少用foreach,因爲每次foreach爲產生一個enumerator(約16B的內存分配),儘量改爲for.

5)Lambda表達式,使用不當會產生內存泄漏.

5.儘量少用LINQ:

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

2)會分配大量GC Allow.

6.控制StartCoroutine的次數:

1)開啓一個Coroutine(協程),至少分配37B的內存.

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

3)Enumerator -> 16B.

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

8.緩存組件:

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

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





.框架設計層面。

一個相對中大型的遊戲,系統非常的多。這時候合理的適時的釋放內存有助於遊戲的正常體驗,甚至可以防止內存快速到達峯值,導致設備Crash。
目前主流平臺機型可用內存:
Android平臺:在客戶端最低配置以上,均需滿足以下內存消耗指標(PSS):
1)內存1G以下機型:最高PSS<=150MB
2)內存2G的機型:最高PSS<=200MB
iOS平臺:在iPhone4S下運行,消耗內存(real mem)不大於150MB

1.場景切換時避開峯值。
當前一個場景還未釋放的時候,切換到新的場景。這時候由於兩個內存疊加很容易達到內存峯值。解決方案是,在屏幕中間遮蓋一個Loading場景。在舊的釋放完,並且新的初始化結束後,隱藏Loading場景,使之有效的避開內存大量疊加超過峯值。

2.GUI模塊加入生命週期管理。
 
主角、強化、技能、商城、進化、揹包、任務等等。通常一個遊戲都少不了這些系統。但要是全部都打開,或者這個時候再點世界地圖,外加一些邏輯數據內存的佔用等等。你會發現,內存也很快就達到峯值。
這時候有效的管理系統模塊生命週期就非常有必要。首先將模塊進行劃分:

1)經常打開 Cache_10;
2)偶爾打開 Cache_5;
3)只打開一次 Cache_0。

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



1、  由於實時對戰遊戲的數據包數量巨大,早期版本的幀同步策略會導致比較明顯的卡頓,通過進行數據包的合併與優化逐漸解決了卡頓問題;

2、  頻繁創建和銷燬的小兵對象讓CPU爆表了,大量的小兵如果採用實時內存的分配和回收,會產生大量的內存碎片和系統開銷,解決方法之一就是採用高效的對象池進行優化,對每個內存對象的狀態進行操作即可;

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

4、  在高中低三檔機型上玩遊戲時,分別加載不同層次的特效包,這也有助於降低CPU和內存的開銷;性能分析過程中發現副本內wwise音頻組件佔了30%的CPU時間,果斷拋棄之,採用Unity自帶音頻功能,優化很明顯;

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

6、    資源使用總量是否在合理範圍之內。

7、   一個場景內的資源重複率。

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

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

10、             網格、紋理、音頻、動畫、GameObject等資源是否超標。

11、             貼圖:

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

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

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

15、             l  若干種貼圖合併技術;

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

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

18、              

19、             模型:

20、             l  儘量控制模型的面數,小於1500會比較合適;

21、             l  不同設備使用不同的模型面數;

22、             l  儘量保持在30根骨骼內;

23、             l  一個網格不要超過3個material;

24、             動畫:

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

26、             l  儘量減少骨骼數量;

27、             聲音:

28、             l  採用壓縮MP3 和 wav;

29、             資源方面的優化:

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

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

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

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

34、             l  在關卡內謹慎使用DontDestroyOnLoad,被標註的資源會常駐內存;

35、             代碼的優化:

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

37、             l  用簡單的“for”循環代替“foreach”循環;

38、             l  爲所有遊戲內的動態物體使用內存對象池,可以減少系統開銷和內存碎片,複用對象實例,構建自己的內存管理模式,減少Instantiate和Destory;

39、             l  儘量不使用LINQ命令,因爲它們一般會分配中間緩器,而這很容易生成垃圾內存;

40、             l  將引用本地緩存到元件中會減少每次在一個遊戲對象中使用 “GetComponent” 獲取一個元件引用的需求;

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

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

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

44、             l  要儘量減少Unity回調函數,哪怕是空函數也不要留着;(例如空的Update、FixedUpdate函數)

45、             l  儘量少使用FindObjectsOfType函數,這個函數非常慢,儘量少用且一定不要在Update裏調用;

46、             l  千萬一定要控制mono堆內存的大小;

47、              

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

49、             

50、             主要因素有:

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

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

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

54、             由於 Saved by batching 和 Drawcall 值過大所引起的卡的問題我所做的優化方式有:

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

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

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

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

59、                     4.   對於特效:儘量把材質紋理合並

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

61、              

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

63、              

64、              

65、              

66、             檢測方式:

67、             一,Unity3D 渲染統計窗口

68、             Game視窗的Stats去查看渲染統計的信息:

69、             1、FPS

70、             fps其實就是 framesper second,也就是每一秒遊戲執行的幀數,這個數值越小,說明遊戲越卡。

71、              

72、             2、Draw calls

73、             batching之後渲染mesh的數量,和當前渲染到的網格的材質球數量有關。

74、              

75、             3、Saved by batching 

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

77、             很多GUI插件的一個好處就是合併多個對象的渲染,從而降低DrawCalls ,保證遊戲幀數。

78、              

79、             4、Tris 當前繪製的三角面數

80、              

81、             5、Verts 當前繪製的頂點數

82、              

83、             6、Used Textures 當前幀用於渲染的圖片佔用內存大小

84、              

85、             7、Render Textures 渲染的圖片佔用內存大小,也就是當然渲染的物體的材質上的紋理總內存佔用

86、              

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

88、              

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

90、              

91、             10、VisibleSkinned Meshes 蒙皮網格的渲染數量

92、              

93、             11、Animations 播放動畫的數量

94、             注意事項:

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

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

97、             一般來說,要做到:

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

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

100、          2,FPS,每一秒遊戲執行的幀數,這個數值越小,說明遊戲越卡。

101、          3,Render Textures 渲染的圖片佔用內存大小。

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

103、           

104、          二,代碼優化

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

106、          比如:

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

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

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

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

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

112、           

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

114、           

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

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

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. 優化數學計算

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

128、           

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

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

131、          7,減少GetComponent的調用

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

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,避免分配內存

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

149、           

150、          9,使用iOS腳本調用優化功能

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

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

153、           

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

155、           

156、          10,

157、          優化垃圾回收

158、           

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

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

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

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

163、          {

164、          System.GC.Collect();

165、          }

166、           

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

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

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

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

188、           

189、          System.GC.Collect();

190、           

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

192、           

193、          三,模型

194、          1,壓縮 Mesh

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

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

197、          2,避免大量使用 Unity 自帶的 Sphere 等內建 Mesh

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

199、           

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

201、          屏幕特效

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

203、          實時的陰影

204、           

205、          2優化建議

206、          2.1渲染

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

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

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

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

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

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

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

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

215、          9.使用occlusionculling

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

217、          12.使用drawcallbatching:

218、                  對於相鄰動態物體:如果使用相同的shader,將texture合併

219、                  對於靜態物體,batching要求很高,詳見Unity Manual>Advanced>Optimizing Graphics Performance>Draw Call Batching

220、           

221、          規格上限

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

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

224、          3.      骨骼數量不要超過30

225、          4.      面數在1500以內將得到好的效率

226、          2.2物理

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

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

229、          3.不要使用meshcollider

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

231、          2.3腳本編寫

232、          1.儘量不要動態的instantiate和destroyobject,使用object pool

233、          2.儘量不要再update函數中做複雜計算,如有需要,可以隔N幀計算一次

234、          3.不要動態的產生字符串,如Debug.Log("boo"+ "hoo"),儘量預先創建好這些字符串資源

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

236、          5.儘量減少函數調用棧,用x= (x > 0 ? x : -x);代替x = Mathf.Abs(x)

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

238、           String的相加操作,會頻繁申請內存並釋放,導致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、          在函數中動態new array,最好將一個array、傳進函數裏修改

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.數據類型

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.少使用的函數:pow,sin,cos等

265、          2.4 GUI

266、          1.不要使用內置的onGUii函數處理gui,使用其他方案,如NGUI

267、           

268、          3.格式

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

270、          最簡單的優化建議:

1.PC平臺的話保持場景中顯示的頂點數少於200K~3M,移動設備的話少於10W,一切取決於你的目標GPU與CPU。
2.如果你用U3D自帶的SHADER,在表現不差的情況下選擇Mobile或Unlit目錄下的。它們更高效。
3.儘可能共用材質。
4.將不需要移動的物體設爲Static,讓引擎可以進行其批處理。
5.儘可能不用燈光。
6.動態燈光更加不要了。
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(圖形處理器)優化?

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


2.CPU與GPU的限制

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

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

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

關於Profiler介紹:http://docs.unity3d.com/Documentation/Manual/Profiler.html

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

3.關於頂點數量和頂點計算

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

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

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



4.針對CPU的優化——減少DRAW CALL 的數量

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

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

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

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


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

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

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

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

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

假設合併的兩個物體(手動合併)但不共享材質,不會有性能表現上的提升。多材質的物體相當於兩個物體不用一個貼圖。所以,爲了提升CPU的性能,你應該確保這些物體使用同樣的貼圖。

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

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

e.使用相關剔除數量直接減少Draw Call數量,下文有相關提及。


5.優化幾何模型

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

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


6.關於光照

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

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

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


7.對GPU的優化——圖片壓縮和多重紋理格式

Compressed Textures(圖片壓縮):

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

圖片壓縮將降低你的圖片大小(更快地加載更小的內存跨度(footprint)),而且大大提高渲染表現。壓縮貼圖比起未壓縮的32位RGBA貼圖佔用內存帶寬少得多。

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

MIPMAps(多重紋理格式):

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

跟網頁上的略縮圖原理一樣,在3D遊戲中我們爲遊戲的貼圖生成多重紋理貼圖,遠處顯示較小的物體用小的貼圖,顯示比較大的物體用精細的貼圖。這樣能更加有效的減少傳輸給GPU中的數據。


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

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

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

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

以下是這幾個優化技術的相關使用和介紹:

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.關於Realtime Shadows(實時陰影)

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

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


10.對GPU優化:採用高效的shader

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

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

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

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

在CG/HLSL中--

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

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


11.另外的相關優化:

a.對Draw Call Batching的優化
http://docs.unity3d.com/Documentation/Manual/DrawCallBatching.html

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

c.角色模型的優化建議
用單個蒙皮渲染、儘量少用材質、少用骨骼節點、移動設備上角色多邊形保持在300~1500內(當然還要看具體的需求)、PC平臺上1500~4000內(當然還要看具體的需求)。

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



渲染順序

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

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

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

1.渲染A,使用材質1 
2.渲染B,使用材質1 
3.渲染C,使用材質2

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

1.渲染A,使用材質1 
2.渲染C,使用材質2 
3.渲染B,使用材質1

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

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

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

在這個基礎上,更細的規則有:

場景中的東西,我們使用Z軸來進行空間的劃分,例如背景層,特效層1,人物層,特效層2 
NGUI中的東西,我們統一使用Depth來進行空間的劃分 
人物模型,當人物模型只是用一個材質,DrawCall只有1,但是用了2個以上的材質,DrawCall就會暴增(或許對材質的RenderQueue 進行規劃也可以使DrawCall只有2個,但這個要拆分好才行),3D人物處於複雜3D場景中的時候,我們的空間規則難免被破壞,這只能在設計的時候盡 量去避免這種情況了 
使用了多個材質的特效,在動畫的過程中,往往會引起DrawCall的波動,在視覺效果可以接受的範圍內,可以將特效也進行空間劃分,假設這個特效是2D顯示,那麼可以使用Z軸來劃分空間

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