Unity3D 記憶體優化(一)物件池
一、定義:
關於U3D記憶體優化,一直是遊戲開發者頭疼的事情,由於在專案中我們會頻繁地建立和銷燬一些物件,例如:怪物模型或者是UI預設體,但是,部分物件在遊戲中是會頻繁出現的,例如戰鬥中的小怪物,假如每次都在使用的時候建立新物件,使用完畢後又直接銷燬,這樣對於遊戲的記憶體消耗是很巨大的。
設想一下,我們是否能做一個池子,將一些常用的物件在建立之後放入池中,每次使用都直接從池子中查詢獲取,這樣在提高物件利用率降低重複建立物件時的記憶體消耗的同時,對於效能提高有很大的幫助,這樣的池子就是我們所要說的——物件池。
物件池——就是將物件儲存在一個池子中,當需要時再次使用,而不是每次都例項化一個新的物件。
二、使用物件池有幾個注意點:
1.雖然物件池可以優化物件利用率,但是物件池也不能無限地儲存物件,這樣對於記憶體佔用也是急劇增加,應該通過限制池子上限,並通過統計獲取使用頻率較低的物件並剔除,從而動態地收縮物件池;
2.對於重複使用的物件,在物件池中應該處理好物件重置(reset)的操作;
3.假如在多個執行緒中同時訪問統一物件池,要處理好執行緒安全的問題。
三、實踐:
關於物件池的記憶體優化,我們可以參考記憶體池的設計模式:記憶體池設計與實現
具體如何設計物件池,我們可以通過從AssetStore獲取成熟的外掛,例如:
具體步驟:
1.將外掛.unitypackage包匯入工程中;
2.在Hierachy中新建一個Empty物件,選中該Empty物件,然後選擇選單欄的Component—>Path-o-logical—>Pool Manager—>Spawn Pool:
3.完成上述步驟後,在Empty物件下會繫結一個Spawn Pool元件,接下來我們就需要進行基本的資訊設定:
同一個物件池可以存放不同預設體,每個預設體我們都可以為其建立一個型別欄,同一預設體創建出來的物件歸屬於同一欄。
4.Spawn中各個屬性的作用:
PoolName:快取池的唯一名稱。
MatchPoolScale:勾選後例項化的遊戲物件的縮放比例將全是1,不勾選擇用Prefab預設的。
MachPool Layer:勾選後例項化的遊戲物件的Layer將用Prefab預設的。
Don’t Reparent:勾選後例項化的物件將沒有父節點,通通在最上層,建議不要勾選。
Don’t Destroy On Load:這個就不用我解釋了吧?切換場景不釋放。
Log Messages: 是否列印日誌資訊
Pre-Prefab Pool Options :快取池列表,意思就是快取列表裡面可以放各種型別的Prefab。右邊有個 “+”按鈕點選就新增每個型別的Prefab了
prefab:可以直接把工程裡的Prefab直接拖進來。
preloadAmount:快取池這個Prefab的預載入數量。意思為一開始載入的數量!
preloadTime:如果勾選表示快取池所有的gameobject可以“非同步”載入。
preloadFrames:每幾幀載入一個。
preloadDelay:延遲多久開始載入。
limitInstance:是否開啟物件例項化的限制功能。
limit Amount:限制例項化Prefab的數量,也就是限制緩衝池的數量,它和上面的preloadAmount是有衝突的,如果同時開啟則以limitAmout為準。
limitFIFO:如果我們限制了快取池裡面只能有10個Prefab,如果不勾選它,那麼你拿第11個的時候就會返回null。如果勾選它在取第11個的時候他會返回給你前10個裡最不常用的那個。
cullDespawend:是否開啟快取池智慧自動清理模式。
cull Above:快取池自動清理,但是始終保留幾個物件不清理。
cull Delay:每過多久執行一遍自動清理,單位是秒。從上一次清理過後開始計時
cullMaxPerPass:每次自動清理幾個遊戲物件。
自動清理:就是當池子裡面的物件setActive(false)也就是目前不用的時候,poolManager會根據上述引數自動清理這些物件,清理也就是Destroy()掉。
active變為true或false是由Spawn和Despawn方法決定的!
limitInstance是否開啟(打鉤)的區別:
不開啟情況:假如此時preloadAmount為1,如果使用者想要每隔5秒去Spawn一個緩衝池中的物件,那麼當載入第二個prefab物件的時候,緩衝池會再建立一個此物件,如果程式再Spawn,那麼還會在產生一個這樣的物件,就這樣一直產生下去,Spawn幾次就產生幾個物件!
(粒子系統:迴圈時:Spawn幾次,就會產生幾個物件,不迴圈時(迴圈結束狀態為false):會再產生一個物件,如果此時的Spawn速度特別快,並且檢測不到前面的物件狀態為false,那麼可能會產生多個物件,直到檢測到前面的幾個物件有false狀態,那麼產生物件到此為止,程式在此幾個物件之間來回Spawn!)
開啟情況:
此時Limit Amount為1
limitFIFO勾選:那麼程式永遠使用的是預載入物件,而不會再產生其他物件!當載入第二次的時候,即使第一個物件處於true狀態,也使用它,即操作第一個物件!(粒子系統為迴圈或者不迴圈時,效果和這一樣)
limitFIFO不勾選:那麼程式永遠使用的是預載入物件,而不會再產生其他物件!當載入第二次的時候,那麼必須等第一個物件變為false狀態,才能使用它!如果過了5s,第一個物件還沒變為false狀態,那麼程式會報錯!(粒子系統為迴圈:會迴圈下去,只有一個預載入物件,不報錯,不迴圈時:等上一個變為false才能進行第二次,只有一個預載入物件,不報錯!)
如果limit Amount數量大於1,為10的話
limitFIFO勾選:永遠只有10個物件產生,當載入第11個物件時,如果前十個物件沒有一個active為false狀態,那麼程式會選擇不常用的那個,從而避免報錯!
limitFIFO不勾選:永遠只有10個物件產生,但是當載入第11個物件時,如果前十個物件沒有一個active為false狀態,那麼程式會報錯!
(粒子系統,永遠只有10個物件,不報錯!)
5.設定完以上引數之後,我們開始在程式碼中進行物件新增到物件池和從物件池中取出物件的操作:
a. 獲取物件池:
private SpawnPool mActors_Pool;
mActors_Pool = PoolManager.Pools["ActorsAndBullets"];
b.獲取物件池中物件的方法:
Transform bulletPrefab = mActors_Pool.prefabs["Bullet"];
Transform bullet = mActors_Pool.Spawn(bulletPrefab);
c.銷燬物件池中物件的方法:
mActors_Pool.Despawn(bullet);
d.全部銷燬的方法:
mActors_Pool.DespawnAll();