在今年的CJ CGDC 中國遊戲開發者大會上,來自Unity大中華區的技術支持經理張鑫帶來了關於《全新的Unity移動遊戲優化解決方案》的精彩主題演講。本次演講分享的內容,包括從渲染模塊、物理模塊、動畫模塊的CPU優化;如何對堆內存的管理以及面對內存泄露和資源冗餘的解決方案;以及對代碼的優化處理。
首先通過Profiler來找到具體的瓶頸,通過Profiler可以看到每一幀裏每個函數的具體開銷。
如果我們的代碼邏輯很複雜,上萬行,那麼可以藉助Begin/EndSample 來拆分,得到真正開銷大的代碼塊。
在移動設備上,半透明渲染的開銷是需要特別注意的(像花,樹,草等),因爲在移動設備上會造成更多的overdraw
不同的設備對Drawcall的敏感度不同,因此可以針對設備做一個“LOD”,在高端機上允許更多的drawcall(即可以開啓更多的特效等)。
MeshSkinning.Update 是蒙皮計算的開銷,Animator.Update 是骨骼動畫的更新開銷。OptimizeGameObject的優化選項默認是關閉的。在紅米上,一個100人的測試案例中,開啓之後,前者可以提高70%的效率,而後者可以提高30%的效率。
另一個測試也是在紅米上,200人同屏,單個角色400個頂點,560面左右,動畫的總時長爲6秒,骨骼數量平均爲12個。通過序列幀的形式實現skinnedMeshRenderer到MeshRenderer 的轉換。可以跑到25幀。
BakeMesh (SkinnedMeshRenderer.BakeMesh)
-
200+
-
~400 頂點
-
~500+ 60 面
-
~6 s 動畫
-
~12 骨骼
上圖是一個Ui的例子,由一位工程師在兩週左右的時間完成,美術除外(來自scaleform在Assetstore上的資源)。
UI系統的渲染順序是由UI元素在Hierarchy中的順序決定的。而Ui系統會通過重排UI元素的渲染順序來減少drawcall,但前提是不改變渲染結果。因此發生重疊之後,drawcall可能會上升。
遊戲製作的中後期通常會開始遇到內存上的問題。
-
總體內存:
•Used Total:當前幀的Unity內存、Mono內存、GfxDriver內存、Profiler內存的總和。
-
Mono內存:
-
記錄代碼堆內存的分配情況,由Mono控制
-
內存只增不減
-
建議<40MB
Mono內存有80%的團隊都不太關心,但卻是很重要,會影響遊戲的流暢性。
-
常見問題:Log輸出
StackTraceUtility.PostprocessStacktrace ()
StackTraceUtility.ExtractStackTrace()
Log的輸出不僅會消耗CPU,同時也會引起較大的堆內存分配。
-
內存泄露
-
資源被強行Hold無法釋放
-
表現症狀:Profiler中內存增長趨勢明顯,且資源無法回收
-
DetailedMemory Profiler
-
資源冗餘
-
資源通過AB加載,且資源在AB建立時存在多份
-
對AB打包機制進行詳細排查
-
依賴關係打包
-
遊戲優化深度檢測:
-
通過Profiler逐幀查看和分析CPU佔用較高的函數
-
GC.Alloc
-
Shader.Parse
-
Shader加載時的解析耗時
這部分耗時的避免,可以通過將shader打入單獨的assetbundle包,並在進入場景時就進行預加載。
同一時間Load過多的非“基礎”AB,且不及時Unload,會導致過多的內存消耗,如WebStream 和Serialized File 的內存。
而PersistentManager.Remapper 的意義是:PersistentManager維護資源的持久化存儲功能,Remapper保存的是加載到內存的資源heapID與源數據FileID的映射關係,但Remapper是memory pool,只增不減,因此建議不要一次性加載過多的assetbundle,而是以流式的方式來加載,以減小其峯值。
小結:性能優化是沒有定式的,它需要“因時因地”制宜。