Unity游戏中的一些规范和优化建议
一.代碼規范和建議
避免Update LateUpdate等函數內頻繁的GC Alloc,避免在Update和LateUpdate內有以下操作:
調用GetComponet()
調用FindObjectsOfType()
使用GameObject.Tag和GameObject.Name
等等其他有堆內存分配的操作
避免頻繁調用ToString()分配內存
避免OnGUI的調用
禁止使用Debug.Log打印log,用框架中的LogManager統一管理
避免使用枚舉或struct做key進行字典查找(除非使用定制的comparer)他們的GetHashCode都有裝箱操作,每次調用TryGetValue查找都會有內存分配.
在使用協程時盡量復用WaitXXX對象,而不是每次分配
頻繁創建和更新的字符串盡量緩存,比如CD時間等
盡量避免dict.Values操作,直接遍歷取Values即可
使用CompareTag代替GameObject.Tag
用RayCastNoAlloc替換RayCastAll
用yield return null 代替 yield reture 0
如果文件中不需要Update或Start函數等,刪掉對應的空函數
盡量少用lambda,創建帶upvalue的lambda對象會產生124B的GC,禁止在Update或for循環中使用
使用數組的數組,而不是多維數組。如int[i][j]的數組的數代替int[i,j]的多維數組
盡可能避免使用攜程
禁止使用接受字符串參數的GetComponent等類似函數的重載方法,使用泛型類型的
代碼中需要訪問到 Transform 組件的位置數據時, 盡可能使用 localPosition 代替對 position 屬性的訪問. localPosition存儲在transform中,訪問該值時,Unity會直接將其返回,而position在每次訪問時都會重新計算,如果要經常獲取position,可以將其緩存起來。
避免使用LINQ表達式,部分功能無法在某些平臺上使用,會分配大量GC Alloc。
禁止高頻的字符串拼接. 如無法避免, 必須使用 StringBuilder 代替 "+" 操作符進行字符串拼接.
Delegate 回調方法必須適時的解除注冊, 否則回調方法所屬的對象會一直有引用計數, 繼而引起該對象所引用的資源無法得到釋放.
盡量避免使用 Reflection(反射).
盡量避免使用可變參數(param object[] args), 避免裝箱拆箱.
簡單條件判斷盡量使用三目運算符: b?x:y.
注意 List 等容器常用接口的復雜度, 盡量從尾部移除/批量移除(RemoveRange)等.
在頻繁查詢數據列表時, 建議使用 HashSet/HashTable 查找時間復雜度低的數據結構, 避免使用 List.
盡可能為快速產生和消滅的大量對象建立緩沖池.
盡可能將類或函數聲明為 sealed, IL2CPP 會對 sealed 的類或函數進行優化, 變虛函數調用為直接函數調用.
盡量減少new的次數,預分配/成員變量替代臨時new變量等.
最快的空串比較方法:最快的方法是str.Length == 0其次是str == String.Empty或str == "" 注:C#在編譯時會將程序集中聲明的所有字符串常量放到保留池中(intern pool),相同常量不會重復分配。(指的是這個"")
減少Find方法使用。Find()方法會遍歷內存中的每個GameObject和組件,隨著項目規模的擴張,它的開銷將會越來越大。不要頻繁的使用Find()和與其類似的方法,可以考慮在Inspector中設置對對象的引用,或者創建一個專門用于管理需要搜索的對象的引用的腳本。
避免使用Camera.main。 Camera.main 因此遭遇了和 Find() 一樣的問題:在內存中搜索了所有的 GameObjects 和 Components,這個使用可能會消耗大。
嘗試使用sqrMagnitude(即magnitude的平方)替代magnitude,減少開平方操作。
盡量少用模運算和除法運算,比如a/5f,要寫成a*0.2f。
盡量避免在運行時為Transforms重新設置parent。Unity對于在同一個parent下的所有Transforms,內存排布是連續的類似動態數組,運行時重新設置parent可能會導致數組重新分配
變量和字段如果可以聲明為const應該總是被聲明為const,如果無法使用const,嘗試使用readonly
二.其他規范和建議
禁止非圖集貼圖資源不合理的留白. 會影響 UGUI 運行時自動合批.
資源設置 UI 的貼圖資源禁止勾選 Generate Mip Maps.
禁止使用修改 Alpha 值的方式來隱藏界面.
盡可能降低 Release 版中圖集留白, 提高貼圖利用率.
建議同一 Canvas 中使用到的圖集數量控制在三個以內.
建立合理規劃公共圖集. 在內存占用/加載頻度/引用復雜度之間確定合理的平衡點.
禁止匿名 GrabPass. 如需使用到 GrabPass 必須命名并盡可能復用.
Shader中盡可能減少多 Pass 渲染, 除非必須這么做.
盡可能降低 Release 版中 Shader 中的 Keyword 數量.
盡可能降低 SkinnedMeshRenderer 組件數量.
禁止逐幀直接使用名稱對 Shader Uniform 量進行更新
盡量避免在 Shader 中使用復雜的計算如: pow/sin/cos/tan/log 等.
建議在 Shader 中采用預混合或實時混合紋理的方式代替實時地多次紋理采樣
移動平臺的 Shader 編碼一定要考慮數據精度(float/half/fixed)的合理使用.
應盡可能減少每幀 Material.GetXX/Material.SetXX的次數, 比如把多個 uniform half 變量合并為 uniform half4.
盡可能避免將 Animator 的 Culling Mode 屬性設置為 Always, 對于不使用 RootMotion 的項目建議選擇 CullCompletely.
盡可能避免使用物理引擎. 建議自行編碼模擬物理效果
使用物理模塊的游戲, 建議在 PhysicsManager 中設置矩陣, 會有較大的性能提升
如使用第三方音頻插件, 需禁用 FMOD 模塊(Edit -> Project Settings -> Audio -> Disable -> UnityAudio).
若不需要立體音效, 音頻導入設置需勾選 Force to Mono(注: 制作音頻時就應該制作單通道的.)
音頻格式: iOS平臺一般使用 mp3, Android平臺一般使用 ogg.
無需由邏輯代碼訪問的渲染資源禁止勾選 Read/Write Enabled, 如網格和圖片.
導入蒙皮網格模型時建議勾選 Optimize GameObject 優化選項, 可極大地降低骨骼層次復雜度, 優化 CPU 性能.
無動作模型資源必須將 Animation Type 設置為 None, 否則會導致游戲對象掛在不必要的動畫腳本, 大量的話會嚴重影響 CPU 性能.
導入的模型如果無需用到法線和切線, 必須將導入設置的 Normals 和 Tangents 選項設置為 None.
導入的模型如果無需參與Unity Lightmap 烘焙, 必須將導入設置的 Generate Lightmap UVs 選項設置為 None.
禁止在 Release 版中存在任何 OnGUI 相關代碼
禁止在 Release 版中存在任何日常調試相關的 UnityEngine.DeBug 類日志輸出
禁止在 Release 版中開啟 "Development Build" 和 "Script Debugging" 選項
在發布時必須將游戲鎖定至合適的幀率(建議 30/60 幀)
禁止在 Release 版中使用引擎提供的 SendMessage 方法.
?
總結
以上是生活随笔為你收集整理的Unity游戏中的一些规范和优化建议的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity项目代码书写规范
- 下一篇: 极品飞车OL强力B车推荐与属性参数一览