Object Pool pattern
物件池(Object Pool
):是一種減輕大量建立並銷毀物件時 CPU 負擔的設計模式。使用物件池時,物件會先被建立並放入池中等待,需要時應用程式不會新建物件,而是從物件池中取得並啟用它。當使用完畢後,物件不會被銷毀,而是被放回物件池中。
改進
- 可以在程式載入時建立物件池,這樣使用者就不會感到卡頓。
- 考慮將物件池設為static或是singleton的:這樣可以在所有情況下方便呼叫使用。
- 使用Dictionary來管理多個物件池:如果有多個物件池,可以使用 Key-Value 的資料結構(如 Dictionary)來管理,只需根據對應的 Key 就能方便地取得所需的物件池。
- 注意釋放物件池中的物件:確保物件在物件池中時不會被釋放,避免執行期間發生錯誤。
- 設定物件池上限:物件過多會消耗大量記憶體,因此需要為物件池設定一個上限,避免物件池中物件過多。
以下是一個簡單的物件池,
- 這個物件池使用Stack
- SetupPool()用來產生一些物件放到物件池中
- GetPooledObject()用來啟用物件並取得,當物件不足(stack.Count == 0)時,會再產生一個新物件
- ReturnToPool()用來將物件停用,並返回物件到物件池
- 注意不要直接使用,因為這個例子沒有設定Stack上限,可能會產生過多的物件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44public class ObjectPool : MonoBehaviour
{
[private uint initPoolSize; ]
[private PooledObject objectToPool; ]
// 存儲物件池中的物件
private Stack<PooledObject> stack;
private void Start()
{
SetupPool();
}
// 建立物件池(在卡頓不明顯時調用)
private void SetupPool()
{
stack = new Stack<PooledObject>();
PooledObject instance = null;
for (int i = 0; i < initPoolSize; i++)
{
instance = Instantiate(objectToPool);
instance.Pool = this;
instance.gameObject.SetActive(false);
stack.Push(instance);
}
}
// 從物件池中返回第一個可用的物件
public PooledObject GetPooledObject()
{
// 如果物件不夠,則實例化新的物件
if (stack.Count == 0)
{
PooledObject newInstance = Instantiate(objectToPool);
newInstance.Pool = this;
return newInstance;
}
// 否則,從物件池中取得下一個物件
PooledObject nextInstance = stack.Pop();
nextInstance.gameObject.SetActive(true);
return nextInstance;
}
public void ReturnToPool(PooledObject pooledObject)
{
stack.Push(pooledObject);
pooledObject.gameObject.SetActive(false);
}
}
建立一個 PooledObject 類別,讓它依賴 ObjectPool,這樣可以在使用完畢後返回物件池:
1 | public class PooledObject : MonoBehaviour |
在Unity 2021後,有內建 UnityEngine.Pool 物件池,它提供了多種容器的Pool,像是
- ObjectPool
:是一個Stack - DictionaryPool<T0,T1>
- GenericPool
- HashSetPool
- LinkedPool
- ListPool
使用內建 UnityEngine.Pool 物件池有許多好處,包含 - 可以快速的使用物件池,不需要重複造輪子
- 有多種容器供你選用
- 當到達最大物件數量後會摧毀該物件,不建立過多的物件
以下是UnityEngine.Pool的範例
1 | using UnityEngine.Pool; |
與上面簡單的ObjectPool相同,讓RevisedProjectile依賴 IObjectPool<RevisedProjectile>
,在使用完畢後呼叫objectPool.Release(this)
返回物件池:
1 | public class RevisedProjectile : MonoBehaviour |