這是摘自Unity官方文檔有關優化的部分,原文鏈接:https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity.html
總共分為如下系列:
- 采樣分析
- 內存部分
- 協程
- Asset審查
- 理解托管堆 【推薦閱讀】
5.1 上篇:原理,臨時分配內存,集合和數組
5.2 下篇:閉包,裝箱,數組 - 字符串和文本
- 資源目錄
- 通用的優化方案
- 一些特殊的優化方案
真實項目中大部分錯誤都是無心之過——臨時的測試改動或者某個非常累的開發者不小心設置錯了資源導入設置,造成資源出現大問題。
對于規模很大的項目,需要加入對抗人為錯誤的首條防線。例如,寫幾行代碼確保沒有人可以給工程提交了一個4K的未壓縮紋理。你可能覺得很意外,但是這是一個非常常見的錯誤。一個4K的未壓縮紋理會占據超過60MB的內存資源。在比較低端的設備上,例如iPhone4S上,超過180-200MB的內存就比較危險了。如果錯誤地添加了這個問題,1/4~1/3的內存就被占用了,也會造成非常難檢測的內存溢出問題。
雖然在5.3之后,可以用內存剖析工具找到這個問題,但是還是最好在一開始就防止類似這樣的問題發生。
使用AssetPostprocessor
Unity編輯器中的AssetPostprocessor類可以用來確定Unity工程中的資源是否滿足標準。當資源被導入的時候,這個類會收到回調。使用這個類的方法只需要繼承,自己實現多個OnPreprocess方法即可。重要的方法有:
- OnPreprocessTexture
- OnPreprocessModel
- OnPreprocessAnimation
- OnPreprocessAudio
具體可以參見AssetPostProcessor,可以知道更多的OnPreprocess方法。
public class ReadOnlyModelPostprocessor : AssetPostprocessor
{
public void OnPreprocessModel()
{
ModelImporter modelImporter = (ModelImporter)assetImporter;
if(modelImporter.isReadable)
{
modelImporter.isReadable = false;
modelImporter.SaveAndReimport();
}
}
}
上面是應用AssetPostprocessor的一個例子。
當項目中導入模型的時候或者模型的導入設置被更改的時候,這個方法會被調用。這個方法會檢測Read/Write屬性,如果屬性為true,強制改成false,保存并且重新導入。
記住,SaveAndReimport方法會導致這個代碼又被執行一次,因為會強制將屬性改為false,所以不會進入到無限循環。
這個改動的原因可以參見模型部分。
常見的資源規則
紋理Texture
關閉read/write enabled的標志
Read/Write enabled標志導致Texture會在內存中存在兩份資源:一份在GPU,一份在GPU尋址空間【因為在大部分平臺,從GPU內存讀取非常緩慢。從GPU內存讀取紋理資源到臨時緩沖區非常不劃算】。在Unity中,這個選項默認被關閉,但是有可能被意外開啟。
Read/Write enabled屬性被打開只有在Shader外操作紋理數據才有意義(例如Texture.GetPixel和Texture.SetPixel等)所以應當盡可能避免開啟。
盡可能關閉Mipmaps
如果對象相對于Camera的Z深度不變,最好關閉mipmaps可以節省大概1/3的內存。如果對象的Z深度值會變化,關閉mipmaps可能導致GPU的取樣效果比較差。
通常來講,UI紋理和在屏幕上是固定大小的紋理資源通過可以關閉掉mipmaps選項。
壓縮所有的紋理
對于目標平臺使用合理的紋理壓縮格式可以節省內存資源。
如果目標平臺的紋理壓縮格式設置的不合理,Unity加載紋理資源之后會對紋理資源重新解壓,會消耗大量CPU時間和內存資源。這對于Android設備很常見,通常是因為不同的芯片級支持不同的紋理格式,差異太大,難以統一。
建立紋理限制條件
雖然這個很簡單,但是通常很容易忘記重新調整紋理的大小,或者不小心改變了導入設置中的紋理大小。最好是通過代碼確定不同類型的紋理資源是否滿足了設定的規則。
模型Model
關閉Read/Write enabled的標志
Read/Write enabled標志對于模型而言和Texture中的道理相同。但是,對于模型而言,Unity對于這個選項默認開啟。
當項目會在運行過程中通過代碼修改Mesh,或者Mesh被用來作為MeshCollider組件的基礎時,Unity要求這個屬性必須開啟。如果模型沒有用到MeshCollider中或者不需要通過代碼修改,將這個屬性改成false可以節省一半的內存資源。
對于非Character類的模型關閉rigs
默認情況下,Unity會為非Character模型導入一個原生的rig。這樣會導致如果在運行過程中實例化這個模型,Animator組件會被添加。如果這個模型不需要通過Animation系統控制動畫,就會增加額外的開銷。因為所有處在活躍狀態下的Animator會在每幀被觸發。
對于不需要動畫的模型最好關閉這個屬性防止增加額外的Animator組件和場景中出現不需要的動畫效果。
對于有動畫效果的模型開啟Optimize Game Objects選項
Optimize Game Object選項對于有動畫的模型有非常大的性能影響。當這個選項被關閉之后,當模型被實例化的時候,Unity會創建一個大的Transform層級用來映射模型的骨架結構。這個transform的Update開銷會非常大,尤其是有其他的組件(如例子系統或者碰撞器)掛接在上面的時候。這個屬性同時也會影響Unity多線程處理蒙皮和骨骼動畫運算的能力。
如果某個模型骨架的一部分需要被暴露出來,如某個模型的雙手部分需要控制武器,那么這些部分可以列入Extra Transforms列表的白名單。
更多的細節可以參見Unity教程的模型導入部分。Model Importer
盡可能使用Mesh壓縮
啟用網格壓縮之后,用來表示模型數據的不同頻道的浮點數的位數會減少。這樣雖然會引起精度的丟失,所以最終的效果需要被美術同學認可才能放入最終的工程中。
具體的浮點數的位數和壓縮等級之間的關系可以參見Scripting API的ModelImporterMeshCompression.
需要注意的是,不同的頻道可以使用不同的壓縮等級,所以在項目中可以選擇壓縮切線和法線而不壓縮UV和頂點位置。
注意:網格渲染的設置
當向Prefab或者GameObject添加網格渲染器的時候,需要注意設置問題。默認情況下,Unity會開啟Shadow casting and receiving,Light Probe sampling,Reflecion Probe sampling和Motion Vector calculation。
如果項目中不需要使用到上面的這些特性,請用腳本確保上面的屬性被關閉。任何執行添加MeshRender的運行代碼也需要確定是否開啟這些屬性。
對于2D游戲而言,如果不小心向場景中加入的MeshRender中shadow選項未被關閉,會給渲染循環中加入額外的shadow pass過程,對于性能來講是浪費。
音頻
針對平臺選擇合適的壓縮選項
確保對于硬件設備選擇合適的壓縮格式。所有的iOS設備硬件支持MP3解碼,而很多Android設備原生支持Vorbis格式。
而且,向Unity中導入未壓縮的音頻格式,Unity當打包工程的時候會壓縮這些音頻文件。所以沒有必要導入壓縮的音頻文件,然后又解壓,這樣只會降低最后音頻的品質。
將音頻強制改成單聲道
很少移動設備有立體揚聲器。在移動設備的工程里,將導入的音頻強制改成單聲道可以節省一半的資源消耗。對于不需要有立體聲效果的音頻也是同樣處理,如UI聲效。
降低音頻碼率
當然這個需要和負責音頻部分的同事進行溝通,最大化降低音頻文件的碼率可以更進一步的減少內存消耗和打包的工程大小。
上篇:協程
下篇:理解托管堆...上
如果覺得文章對您有用,請點個贊唄!???