Unity 優化建議
轉 https://blog.csdn.net/game_jqd/article/details/51899000
使用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:無法優化。
- 許多貼圖採用的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。
- 當載入一個新的Prefab或貼圖,不及時回收,它就會永駐在記憶體中,就算切換場景也不會銷燬。應該確定物體不再使用或長時間不使用就先把物體制空(null),然後呼叫Resources.UnloadUnusedAssets(),才能真正釋放記憶體。
- 有大量空白的圖集貼圖,可以用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、
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軸來劃分空間