unity內存優化教程筆記

優化層面:代碼、貼圖、框架設計


一、代碼優化

1、用for代替foreach

原因:Mono下的foreach頻繁調用容易觸及堆上限,導致GC過早觸發,出現卡頓現象。尤其在update中,用for代替foreach。

2、string

連接兩個字符串的操作使用StringBuilder.Append來代替string。

 String aa="abc";
 aa+="def";
 StringBuilder text=new StringBuilder("abc",10);
 text.Append("def");
原因:每次使用string的時候,都會在內存裏創建一個新的字符串對象,需要爲該對象分配新的空間。

3、gameObject.tag = xxx

使用gameObject.CompareTag("XXX")來代替gameObject.tag = xxx。

原因:gameObject.tag會在內部循環調用對象分配的標籤屬性以及拷貝額外的內存。
4、使用ObjectPool對象池來管理對象,避免頻繁使用的Instance,Destroy。


二、貼圖優化


貼圖優化的效果更明顯。

1、壓縮png、jpg圖片 https://tinypng.com/

2、巧妙通過調整紋理資源,來調整圖的大小。如:ugui九宮格,部分縮小後在unity中拉大。

3、IOS平臺使用PVRT壓縮,Android使用ETC1壓縮(ETC1只能支持非Alpha通道的圖)。

4、mipMap攝像機距離遠近替換不同圖片。

5、減少色彩使用(純色)。


三、框架設計


1、場景切換時加一個loading

當前舊場景內存未釋放的時候,加載新的場景,此時前後兩個場景的內存疊加,很容易達到內存峯值,導致崩潰。如果在中間加一個loading場景,由於loading場景較小,能夠避開內存的大量疊加,當舊場景的內存釋放完,新場景的初始化結束之後,再隱藏掉loading場景。

因此loading的兩個作用是:

(1)避免場景切換時大量內存疊加導致崩潰。

(2)避免後一個場景太大加載過慢卡住的尷尬。


2、把GUI模塊加入生命週期管理

主角、強化、技能、商城、進化、揹包、任務等系統如果全部打開,內存很容易達到峯值;

需要有效地管理系統模塊生命週期:

(1)將模塊進行劃分:Cache_10(經常打開的)、Cache_5(偶爾打開的)、Cache_0(只打開一次的)

(2)創建一個MuduleMananger類,內部的render方法每分鐘輪詢一次,

         如果是Cache_0,一關閉就直接destroy釋放內存;

         如果是Cache_10,10分鐘後自動釋放內存;

         如果是Cache_5,5分鐘後自動釋放內存;

         每次打開模塊,該模塊都重新計時。


代碼佔用的內存少,資源佔用的內存多。

1.資源類型

GameObject,Transform,Mesh,Texture,Material,Shader,noxss和各種其他Assets

2.資源創建方式

(1)靜態引用(初學者):在腳本中加public GameObject變量,然後拖動資源到面板上,然後Instantiate;

(2)Resources.Load(初學者),從Assets/Resources目錄下加載資源;

(3)通過AssetBundle.Load加載(最常用),然後Instantiate;

3.資源銷燬方式

(1)Destroy

(2)AssetBundle.Unload(false),釋放AssetBundle文件內存鏡像,不銷燬Load創建的Assets對象;

         AssetBundle.Unload(true),釋放AssetBundle文件內存鏡像,銷燬Load創建的Assets對象;

(3)Resources.UnloadAsset(Object),釋放已加載的Asset對象;

         Resources.UnloadUnusedAssets,釋放所有未引用的Asset對象;

4.生命週期

例:創建一個場景,場景中創建一個Empty GameObject,上面掛一個腳本來加載tank資源。

在Awake函數中使用協程來創建資源;

(1)使用Resources.Load()加載資源

IEnumerator LoadResources()
{
	//釋放無用資源
	Resources.UnloadUnusedAssets();
	//等待5秒
	yield return new WaitForSeconds(5.0f);
	//加載資源
	GameObject tank = Resources.Load("Tank") as GameObject;
	yield return new WaitForSeconds(0.5f);
	//實例化一個資源
	GameObject.Instantiate(tank, Vector3.zero, Quaternion.identity) as GameObject;
	yield return new WaitForSeconds(0.5f);
	//銷燬一個資源
	GameObject.Destroy(tankInst);
	yield return new WaitForSeconds(0.5f);
	//釋放無用資源
	tank = null;
	Resources.UnloadUnusedAssets();
	yield return new WaitForSeconds(0.5f);
}
結論:Resources.Load一個資源相對於Instantiate一個資源來說消耗的內存非常少,當Destroy之後,內存佔用減少的較少,Material和Texture等都沒有還原,以便於之後繼續的實例化。

(2)使用AssetBundle.Load加載資源

IEnumerator LoadAssets(string path)
{
	//清除乾淨以免影響
	Resources.UnloadUnusedAssets();
	//等待5秒
	yield return new WaitForSeconds(5.0f);
	//創建一個WWW類
	WWW bundle = new WWW(path);
	yield return bundle;
	yield return new WaitForSeconds(0.5f);
	//AssetBundle.Load一個資源
	Object obj = bundle.assetBundle.Load("Tank");
	yield return new WaitForSeconds(0.5f);
	//實例化一個資源
	GameObject tankInst = Instantiate(obj) as GameObject;
	yield return new WaitForSeconds(0.5f);
	//銷燬一個資源
	GameObject.Destroy(tankInst);
	yield return new WaitForSeconds(0.5f);
	//unload resources
	bundle.assetBundle.Unload(false);
	yield return new WaitForSeconds(0.5f);
	//釋放無用資源
	obj = null;
	Resources.UnloadUnusedAssets();
	yield return new WaitForSeconds(0.5f);
}
結論:
通過WWW Load AssetBundle加載一個資源時,會自動加載相應的Mesh、Texture和Material;

通過Resources.Load加載一個資源的時候,只會加載Mesh;

所以通過AssetBundle的方式加載一個資源,實例化的時候內存消耗較小。

3.通過靜態引用的方式加載一個資源

IEnumerator InstResources()
{
	//清除乾淨以免影響
	Resources.UnloadUnusedAssets();
	//等待5秒
	yield return new WaitForSeconds(5.0f);
	//實例化一個資源
	GameObject inst = GameObject.Instantiate(tank,Vector3.zero,Quaternion.identity) as GameObject;
	yield return new WaitForSeconds(1f);
	//銷燬一個資源
	GameObject.Destroy(inst);
	yield return new WaitForSeconds(1f);
	//釋放無用資源
	tank = null;
	Resources.UnloadUnusedAssets();
	yield return new WaitForSeconds(0.5f);
}
結論:靜態綁定的方式和Resources.Load一樣,加載一個資源的時候,只會加載Mesh;

















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章