Asset auditing - 資源審查
許多項目發生效能問題的真正原因只是由於人員操作不當或是試東試西,而不小心改到導入設定影響到導入的資源。(例如最近的gitlab慘案)
對於較大規模的項目,最好准備一道自動的防線防范人為失誤。例如寫一段簡單的檢查程序確保沒有任何人能在項目加入一張沒壓縮的 4K 貼圖。
或許你會覺得不可能,但這問題我們真的很常見。沒有壓縮的 4K 貼圖會占用大約 60mb 的內存空間,在低端的手機設備(例如 iPhone 4s)上,整個項目用掉超過 180mb~200mb 就會很危險。有時候你的游戲在好的手機上跑沒問題,在差的手機上跑會宕機,不一定是硬件的問題。如果犯這種錯誤,這張貼圖會無端占用應用程序四分之一到三分之一的可用內存,造成很難追蹤的內存不足錯誤。
雖然上面有提到可以用 Unity 5.3 的 MemoryProfiler 來追蹤這樣的問題,但最好養成良好開發習慣排除這樣的可能。
使用資源后處里器(Asset Postprocessors)
Unity 編輯器里的 AssetPostprocessor 類別可以用在 Unity 項目上實行某些基本限制。這個類別在資源導入時會收到一個回調。使用方法即繼承 AssetPostprocessor 並實作一個或多個 OnPreprocess 方法,重要的包含:
OnPreprocessTexture
OnPreprocessModel
OnPreprocessAnimation
OnPreprocessAudio
請查詢 AssetPostprocessor API 文件以了解更多關於 OnPreprocess 方法的數據。
1
2
3
4
5
6
7
8
9
10
|
public
class
ReadOnlyModelPostprocessor : AssetPostprocessor {
public
void
OnPreprocessModel() {
ModelImporter modelImporter = (ModelImporter)assetImporter;
if
(modelImporter.isReadable) {
modelImporter.isReadable =
false
;
modelImporter.SaveAndReimport();
}
}
}
|
這是一個簡單的 AssetPostprocessor 限制規則范例
每當導入模型到項目或模型的導入設定(Import settings)被修改時會呼叫這個類別,這里程序只是檢查可否讀寫模型的設置 isReadable 屬性,如果是 true 就會改為 false,存盤后重新導入資源。
請注意,呼叫 SaveAndReimport 會導致這段程序會被再次呼叫!但由於設置已經被改為 false,所以不會無窮遞歸下去。
至於為何要拿掉寫入的設置會在底下的模型章節解釋。
常見資源規則
Textures - 材質
關閉 Read/Write Enabled 設置
這個 Read/Write Enabled 的設置會造成貼圖在內存里變成兩份,一份在 GPU 上一份在 CPU 可以尋址的內存上。這是因為大多數平台,把數據從 GPU 內存讀回 CPU 很慢。從 GPU 內存讀取一張貼圖到暫存區給 CPU 程序用(例如:Texture.GetPixel)會導致效能很差。這個設定在 Unity 里預設是關閉的,但要避免誤勾這個選項。
Read/Write Enabled 只有在 Shader 以外的地方存取貼圖數據(例如:Texture.GetPixel 和Texture.SetPixel 這樣的 API)時才會需要,但盡量避免使用這個功能比較好。
如果可以,盡量不要用 Mipmaps
對於對象相對於鏡頭之間的 Z 軸深度不太會有變化的對象,關閉 Mipmaps 可以省下約三分之一的內存空間,但如果對象在鏡頭看出去的深度會有變化,關閉 Mipmaps 會影響到 GPU 貼圖取樣的效率。
一般來說,關閉這個選項對於 UI 和不太會變大變小的貼圖很有幫助。
壓縮全部貼圖
針對各個不同的平台選用該平台適合的壓縮格式壓縮貼圖,這是節省內存的首要之務。
如果選到目標平台不支持的格式,Unity 會在 CPU 上解壓縮貼圖,消耗 CPU 時間和很多內存。這類問題比較常發生在Android 上,開發者得注意 Android 裝置上各種不同的芯片各支持哪種壓縮格式。
強制貼圖大小
這雖然是件小事但很多人會忘記調整貼圖大小或不小心從改到導入設定里的貼圖大小上限。先決定各種貼圖的合理大小上限然后用程序去強制限制它。
大多數的手機游戲用 2048x2048 或 1024x1024 來存放圖集(Atlas)就夠了,3D 手機游戲的模型貼圖用到 512x512 應該就夠好了。
Models - 模型
關閉 Read/Write Enabled 設置
這個選項的運作原理和貼圖一樣,只不過預設是打開的。
當項目執行時想用程序來修改 Mesh,或者如果 Mesh 要用作 MeshCollider 的話,這里需要打勾。反之如果模型沒用在MeshCollider,也沒用程序來修改 Mesh 的話,關閉這里可以省下一半的內存。
非角色模型關閉骨架功能
在預設情況下,Unity 會幫非角色的模型放入一個通用骨架(Generic rig),如果這個模型執行時被實例化,會導致被加上一個 Animator 組件。如果這個對象沒有用到 Mecanim 動畫系統就會產生不必要的開銷,因為所有啟動中的 Animator 每幀都會被觸發一次。
關閉沒有動畫的對象的骨架來確保他們不會在執行期間被加上 Animator 組件,造成不必要的消耗。
幫帶有動畫的對象開啟 Optimize Game Objects 選項
OptimizeGame Objects 選項對於帶動畫的模型有明顯的效能影響。如果這里沒打勾,在初始化模型時 Unity 會把整個模型的骨架結構對應的 GameObject 階層全部產生出來。這個巨大的 Transform 組件結構更新起來自然很耗效能,尤其是如果結構帶有其他組件(例如粒子系統或碰撞體)。它也會拉低 Unity 多線程對蒙皮(Mesh skinning)和骨架動畫的計算能力。
打勾后所有的骨架對應的 Transform 結構都會被移除,如果模型骨架結構中有特定的部位需要露出方便控制(例如模型的手部要用來握住武器),則可以把它列在“ExtraTransforms”白名單中。
更多數據可以參閱官方手冊關於 Model Importer 的部分。
如果可以,盡量使用Mesh壓縮
開啟 Mesh compression 選項會縮短用來表示模型數據不同信道的浮點數字元長度,這會移除一定的精確度並可能造成可見的變化,使用這個之前最好先讓美術檢查過這種損失在允許范圍內。
各個壓縮等級使用的位長度在 ModelImporterMeshCompression 腳本里有介紹。
請記得,可以針對不同信道使用不同等級的壓縮,所以項目可以只針對切線向量(Tangent)和法向量(Normal)壓縮但不壓縮 UV 和頂點位置。
追記:Mesh Renderer 的設定
當加入一個 Mesh Renderer 到 Prefab 或 GameObjec t時,請小心上面的設定。預設情況下 Unity 會開啟 Shadow casting、Shadow receiving、Light Probe 取樣、Reflection Probe 取樣和動態向量(Motion Vector)的計算。
假如項目不需要上述的功能,記得寫個編輯器腳本關掉素材上的選項,執行期用程序加 MeshRenderer 到對象上也要記得執行相同的規則。
對於一個2D游戲來說,不小心加了一個帶有 Shadowcasting 的 MeshRenderer 到場景里會觸發整個陰影計算循環,這通常是浪費效能。
Audio - 音效
采用平台支持的壓縮設定
采用硬件支持的音源壓縮格式。所有的iOS設備都有 MP3 硬件解壓縮能力,而大多數的 Android 設備都有支持 Vorbis。
此外,可以直接將未壓縮的聲音文件導入 Unity 里,因為 Unity 會在打包項目時會重新壓縮。所以不需要先壓縮再導入Unity,這只會降低音效質量。
強制音效用單聲道
只有少數的手機裝置真的有立體聲喇叭,而將音效強制設定為單聲道能讓內存的消耗減半。就算游戲會輸出部份的立體聲,有些單聲道像是 UI 音效還是可以開啟這個選項。
降低音頻的取樣
調低取樣能進一步降低內存消耗和最終項目的大小,可以和音效設計師協調找出最小最能接受的音源質量。參考SetCompressionBitrate。