1.代碼層面
1.foreach Mono下的foreach使用需謹慎,頻繁調用容易觸及堆上限,導緻GC過早觸發,出現卡頓現象。 特别注意的是在Update中如果非必要,不要使用foreach。盡量可能用for代替foreach.會産生GC Alloc,說明foreach調用GetEnumerator()時候會有堆記憶體上的操作,new 和dispose。 2.string 如果熟悉C++的話,就會了解,每次使用string的時候,都要在記憶體中建立一個新的字元串對象,就需要為該對象配置設定新的空間。特别是在循環中修改string對象,就會頻繁地配置設定空間,這個時候推薦使用StringBuilder.Append等操作來處理。C++中通常也是通過配置設定一個固定的字元記憶體來處理字元串操作。 3.string gameObject.tag會在内部循環調用對象配置設定的标簽屬性以及拷貝額外的内城,推薦使用gameObject.CompareTag("XXX")來代替.tag 4.ObjectPool 使用ObjectPool管理對象,避免重複的Instance,Destroy。
2.貼圖層面
1.通過調整紋理資源,來調整圖的大小。比如:九宮格
2.IOS平台使用PVRT壓縮紋理,Android使用ETC1格式壓縮。但是ETC1不支援Alpha通道的圖檔壓縮。所有一般把Alpha通道圖分離出來,繪制到GPU顯存時,a值從Alpha圖擷取,無Alpha通道的圖就是用ETC1壓縮。 ETC2支援Alpha但是适用機型太少了。
3.minMap 離錄影機遠近不同用不同的圖檔。
4.通過減色的方式減少圖檔大小。許多UI其實用的色彩很少,用不到256色。這類圖檔就可以使用減色壓縮。
3.架構設計
1.峰值要求: Android平台:在用戶端最低配置以上,均需滿足一下記憶體消耗名額(PSS): a)記憶體1G以下機型:最高PSS<=150MB b)記憶體2G機型:最高PSS<=200MB IOS平台:在iPhone4S下運作,消耗内城(real mem)不大于150MB。
2.場景切換時避開峰值 目前一個場景還未釋放的時候,切換到新場景。這時候由于兩個記憶體疊加很容易達到記憶體峰值。解決方案是,在螢幕中間遮蓋一個Loading場景。在舊的釋放完,并且新的初始化結束後,隐藏Loading場景,使之有效的避開記憶體大量疊加超過峰值。
3.GUI子產品加入生命周期管理 主角、強化、技能、商城、進化、背包、任務等等。通常一個遊戲都少不了這些系統。但是要全部都打開,或者這個時候再點世界地圖,外加一些邏輯資料記憶體的占用等待。你會發現,記憶體也很快達到峰值。 這時候有效的管理系統子產品聲明周期就非常有必要。首先将子產品進行劃分: a)經常打開Cache_10; b)偶爾打開Cache_5; c)隻打開一次Cache_0;
建立一個ModuleManager類,内部Render方法每分鐘輪回詢問一次。如果是"Cache_0"這個類型,一關閉就直接Destroy釋放記憶體,如果式“Cache_5"這個類型就5分鐘釋放。Cache_10就10分鐘。每次重新打開就重新計時。
4.資源方面
1.資源類型 GameObject,Transform,Mesh,Texture,Material,Shader,noxss和其他Assets。
2.資源建立方式
- 靜态引用,在腳本中加一個public GameObject變量,在Inspector面闆 拖一個prefab到該變量。
- Resource.Load,資源需要放在Assets/Resources目錄。
- AssetBundle.Load,通過AB加載。
3.資源銷毀方式
- Destroy(GameObject).直接銷毀物體釋放記憶體。
- AssetBundle.Unload(false),釋放AssetBundle檔案記憶體鏡像,不銷毀Load建立的Asset對象。
- AssetBundle.Unload(true),釋放AssetBundle檔案記憶體鏡像同時銷毀已經Load建立的Asset對象。
- Resources.UnloadAsset(Object),釋放已經加載的Asset對象。
- Resources.UnloadUnuserdAssets,釋放沒有引用的Asset對象。
4.生命周期 a.Resources.Load(同靜态引用) Resources.Load一個Prefab相對于Instantiate一個資源來說是一個相對輕量級的操作。上述過程中,Resources.Load加載一個Prefab幾乎沒有消耗記憶體,而Instantiate消耗了2.5M的資源空間。Resouces.Load增加了Mesh和TotalObject的數量,而Instantiate增加了GameObjects,Objects In Scene和Total Objects。 Destroy一個GameObject之後,記憶體有所減少,但是比較少。Instantiate和Destroy前後Material和Texture沒有還原,供以後Instantiate繼續使用。是以需要調用Resurces.UnloadUnusedAssets。
b.AssetBundle.Load 通過WWW Load AssetBundle 的方式加載一個資源會自動加載相應的Mesh,Texture和Material,而通過Resources.Load方式進行加在隻會加載Mesh資訊,是以通過AssetBundle方式加載後,Instantiate一個資源的記憶體消耗比較小。 總結: 使用Resources.Load的時候在第一次Instantiate之前,相應的Asset對象還沒有建立,直到第一次Instantiate的時候才會真正的取讀取檔案建立這些Assets。它的目的是實作一種OnDemand的使用方式,到該資源真正使用的時候才會去建立這些資源。
而是用AssetBundle.Load會直接将資源檔案讀取出來建立這些Assets,是以第一次Instantiate的代價相對比較小。
而Instantiate的過程是一個對Assets進行Clone(複制)和引用相結合的過程。Clone的過程需要申請記憶體存放自己的資料,而引用的過程隻需要直接一個簡單的指針指向一個已經Load的資源即可。例如Transform是通過Clone的。而Texture和TerrainData是通過引用複制的。而Mesh,Material,PhysicalMaterial和noxss是兩者皆有的。 Destroy隻會銷毀Clone出來的Assets并不會銷毀引用的。因為Destroy并不知道是否有其他人還在引用這些東西。是以需要使用Resurces.UnloadUnusedAssets