Unity 使用內建的物件池
分類
說明
原本我有寫一篇 Unity 遊戲物件池 極簡版 的文章,後來發現原來 Unity 內建有物件池的功能,我研究了很久,終於將舊版的物件池移植成 Unity 內建的物件池,本篇將分享 Unity 內建的物件池範例。
關於物件池
關於物件池介紹的部分,請參考 Unity 遊戲物件池 極簡版。
球池
一樣,我需要先建立一個球池。
BallPool.cs
using UnityEngine;
using UnityEngine.Pool;
/// <summary>
/// 球池
/// </summary>
public class BallPool : MonoBehaviour
{
/// <summary>
/// 球預製
/// </summary>
[SerializeField]
private GameObject ballPrefab;
/// <summary>
/// 球池
/// </summary>
private ObjectPool<Ball> pool;
/// <summary>
/// 預設容量
/// </summary>
[SerializeField]
private int defaultCapacity = 10;
/// <summary>
/// 球池
/// </summary>
public ObjectPool<Ball> Pool => pool;
private void Awake()
{
pool = new ObjectPool<Ball>(
CreatePooledItem,
OnTakeFromPool,
OnReturnedToPool,
OnDestroyPoolObject,
true,
defaultCapacity
);
}
/// <summary>
/// 實體化球體
/// </summary>
/// <returns></returns>
private Ball CreatePooledItem()
{
GameObject ballGameObject = Instantiate(ballPrefab, transform);
Ball ball = ballGameObject.GetComponent<Ball>();
return ball;
}
/// <summary>
/// If the pool capacity is reached then any items returned will be destroyed.
/// We can control what the destroy behavior does, here we destroy the GameObject.
/// </summary>
/// <param name="ball"></param>
private void OnDestroyPoolObject(Ball ball)
{
Destroy(ball.gameObject);
}
/// <summary>
/// Called when an item is returned to the pool using Release
/// </summary>
/// <param name="ball"></param>
private void OnReturnedToPool(Ball ball)
{
ball.gameObject.SetActive(false);
}
/// <summary>
/// Called when an item is taken from the pool using Get
/// </summary>
/// <param name="ball"></param>
private void OnTakeFromPool(Ball ball)
{
ball.gameObject.SetActive(true);
}
/// <summary>
/// 取得球,與 Pool.Get() 不同的是,可以設定參數
/// </summary>
/// <param name="position">位置</param>
/// <returns></returns>
public Ball GetBall(Vector3 position)
{
Ball ball = pool.Get();
ball.transform.position = position;
return ball;
}
}
我宣告一個 private ObjectPool<Ball> pool
物件池,內涵 Ball
物件,接著我在 Awake()
實體化 pool 成員 pool = new ObjectPool<Ball>()
。
關於 new ObjectPool<Ball>()
裡面的的參數,請參考 ObjectPool<T0>。
大致參數說明如下:
- 第1個參數是建立物件的方法
- 第2個參數是取得物件的方法
- 第3個參數是回收物件的方法
- 第4個參數是當池滿要摧毀物件的方法
- 第5個參數是如果我們嘗試釋放池中已有的項目,集合檢查將引發錯誤。
- 第6個參數是預設容量
- 第7個參數是容量上限
我還自己寫了一個 GetBall(Vector3 position)
方法,你可以看到裡面有寫 pool.Get()
這就是取得池中物件的方法,但我另外寫的 GetBall()
方法可以額外設定位置。
建立球池遊戲物件
同之前的一樣,建立一個空遊戲物件,新增 BallPool 組件,並設定好屬性。
註:遊戲物件名稱 NormalBallPool 與底下 Ball 腳本 GameObject.Find("BallPool")
不一致。
球物件參考球池
同之前一樣,球腳本中,有一個靜態的球池,它參考的就是遊戲物件的球池,意思是球池是共享的。
Ball.cs
using UnityEngine;
/// <summary>
/// 球
/// </summary>
public class Ball : MonoBehaviour
{
/// <summary>
/// 球池
/// </summary>
private static BallPool ballPool;
/// <summary>
/// 球池
/// </summary>
public static BallPool BallPool
{
get
{
if (ballPool == null)
{
ballPool = GameObject.Find("BallPool").GetComponent<BallPool>();
}
return ballPool;
}
}
/// <summary>
/// 釋放一顆球
/// </summary>
public void Release()
{
ballPool.Pool.Release(this);
}
}
我另位寫了一個 Release()
方法,方便釋放球用。
取得一顆球
要取得一顆球可以這樣呼叫:
Ball ball = Ball.BallPool.Pool.Get();
Ball ball = Ball.BallPool.GetBall(transform.position);
就看是否需要預設位置,當然取得 ball
之後,也是可以在設定位置。
回收一顆球
回收使用的是球池釋放的方法,你可以這樣回收
Ball ball = Ball.BallPool.Pool.Get();
Ball.BallPool.Pool.Release(ball);
ball.Release();
回收具體做些什麼,可以參考 BallPool
類別的 OnReturnedToPool()
。
注意!你不能這樣回收。
Ball ball = Ball.BallPool.Pool.Get();
ball.gameObject.SetActive(false);
因為 Ball.BallPool.Pool.Release(ball);
實際上可能還有執行其他程式,並非只有調用 OnReturnedToPool()
,我會這麼猜測是因為,我曾發生過將所有的球都調用 ball.gameObject.SetActive(false);
但 Ball.BallPool.Pool.CountActive > 0
卻是 true
,我改用 Release()
就恢復正常了,所以保險起見請使用 Release()
。
參考
一杯咖啡的力量,勝過千言萬語的感謝。
支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!