Unity記憶體的優化
這裡從三個緯度來分享下記憶體的優化經驗:程式碼層面、貼圖層面、框架設計層面。
一.程式碼層面。
1.foreach。
Mono下的foreach使用需謹慎。頻繁呼叫容易觸及堆上限,導致GC過早觸發,出現卡頓現象。
特別注意的是在Update中如果非必要,不要使用foreach。儘可能用for來代替foreach。會產生GC Alloc,說明foreach呼叫GetEnumerator()時候有堆記憶體上的操作,new和dispose。
2.string修改。
如果熟悉C++的話,就會了解,每次使用string的時候,都要在記憶體中建立一個新的字串物件,就需要為該新物件分配新的空間。 特別是在迴圈中需要修改string物件,就會頻繁的分配新的空間,這時候推薦使用StringBuilder.Append等操作來處理。C++中通常也是通過分配一個固定的字元記憶體來處理字串的操作。
3.gameObject.tag
gameObject.tag 會在內部迴圈呼叫物件分配的標籤屬性以及拷貝額外的記憶體,推薦使用gameObject.CompareTag("XXX")來代替.tag。
4.使用ObjectPool物件池來管理物件,避免頻繁的Instance,Destroy。
二.貼圖層面。
程式碼上的記憶體優化,很大層面上都不及貼圖上的優化。有時候改一張圖就幫你省了大幾兆的記憶體。
1.巧妙通過調整紋理資源,來調整圖的大小。比如:通過9宮格、部分縮小後Unity裡在拉大等方式。
比如:(主要調整了兩個小元素)就省了一半的記憶體。
優化前
優化後
2.Ios平臺使用PVRT壓縮紋理。Adroid平臺使用ETC1格式壓縮。均可以減至1/4的記憶體大小。優化非常明顯。
目前主流的Android機型基本都支援ETC1格式壓縮。但ETC1只能支援非Alpha通道的圖片壓縮。所以一般把Alpha通道圖分離出來,繪製到GPU視訊記憶體時,a值從Alpha圖裡獲取,無Alpha通道的圖就可以使用ETC1壓縮。
而ETC2以上的格式壓縮雖然支援含Alpha通道的圖片,但是支援的機型還比較少。目前不推薦使用。
未使用ETC1壓縮前的記憶體佔用大小1024*1024的png圖佔用10.7M(包含了Editor中的記憶體佔用,以及mip map記憶體佔用
mipMap是攝像機離得遠近用不同的圖片,3D遊戲中用記憶體換效能的一種有效方式。它會將大圖變成若干小圖,儲存記憶體中,當攝像機離的比較遠的時候,只需使用小圖。
UI、2D場景可以把Texure這個設定去掉。
這樣實際遊戲中未壓縮紋理1024×1024的圖在記憶體中佔用是 4M。(Unity Profiler下看應該是8M)
使用ETC1壓縮後,場景圖片一張大小隻有1.3MB,加上通道圖2.6M。幾乎是用來的1/4。
甚至檔案的大小也小了1/4。
3.通過減色的方式減少圖片大小。很多UI其實使用的色彩很少,用不到256色。這類圖片就可以進行減色壓縮。
三.框架設計層面。
一個相對中大型的遊戲,系統非常的多。這時候合理的適時的釋放記憶體有助於遊戲的正常體驗,甚至可以防止記憶體快速到達峰值,導致裝置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分鐘後自動釋放記憶體。每次開啟模組,該模組就會重新計時。這樣就可以有效合理的分配記憶體。