Unity中对象池的应用ObjectPool

2020年03月20日 20:04 0 点赞 0 评论 更新于 2025-11-21 21:33

为了更好地理解对象池,我们参考了这篇博客中的示例。对象池(ObjectPool)主要用于在游戏中新建和回收对象,避免频繁创建和销毁对象带来的性能开销。

1. 基本对象池类 ObjectPool

public class ObjectPool
{
private Queue<GameObject> m_PoolQueue;
private string m_PoolName;
protected Transform m_Parent;
// 需要缓存的对象
private GameObject prefab;
// 最大容量
private int m_MaxCount;
protected const int m_DefaultMaxCount = 10;

public GameObject Prefab
{
get => prefab;
set => prefab = value;
}

public ObjectPool()
{
m_MaxCount = m_DefaultMaxCount;
m_PoolQueue = new Queue<GameObject>();
}

public virtual void Init(string poolName, Transform transform)
{
m_PoolName = poolName;
m_Parent = transform;
}

public virtual GameObject Get(Vector3 pos, float lifetime)
{
if (lifetime < 0)
{
return null;
}

GameObject returnObj;
if (m_PoolQueue.Count > 0)
{
// 从对象池中取出一个对象
returnObj = m_PoolQueue.Dequeue();
}
else
{
// 池中没有可分配对象了,新生成一个
returnObj = GameObject.Instantiate<GameObject>(prefab);
returnObj.transform.SetParent(m_Parent);
returnObj.SetActive(false);
}

// 使用ObjectInfo脚本保存returnObj的一些信息
ObjectInfo info = returnObj.GetComponent<ObjectInfo>();
if (info == null)
{
info = returnObj.AddComponent<ObjectInfo>();
}

info.PoolName = m_PoolName;
if (lifetime > 0)
{
info.Lifetime = lifetime;
}

returnObj.transform.position = pos;
returnObj.SetActive(true);

return returnObj;
}

// "销毁对象" 其实是回收对象
public virtual void Recycle(GameObject obj)
{
if (m_PoolQueue.Contains(obj))
{
return;
}

if (m_PoolQueue.Count > m_MaxCount)
{
// 对象池已满 直接销毁
GameObject.Destroy(obj);
}
else
{
// 放入对象池
m_PoolQueue.Enqueue(obj);
obj.SetActive(false);
}
}

public virtual void Destroy()
{
m_PoolQueue.Clear();
}
}

上述代码中,ObjectPool 类实现了对象池的基本功能。当从池中取出对象时,会将该对象从队列中移除;对象使用结束后,会通过 Recycle 方法将其回收至对象池。

2. 自定义对象池类 CubePool

public class CubePool : ObjectPool
{
public override GameObject Get(Vector3 pos, float lifetime)
{
GameObject obj;
obj = base.Get(pos, lifetime);

obj.GetComponent<Renderer>().material.color = Random.ColorHSV();

return obj;
}
}

CubePool 类继承自 ObjectPool 类,并重写了 Get 方法,在获取对象时将对象的材质颜色随机设置。

3. 对象信息类 ObjectInfo

public class ObjectInfo : MonoBehaviour
{
public float Lifetime = 0;
public string PoolName;

private WaitForSeconds m_WaitTime;

private void Awake()
{
if (Lifetime > 0)
{
m_WaitTime = new WaitForSeconds(Lifetime);
}
}

private void OnEnable()
{
if (Lifetime > 0)
{
StartCoroutine(CountDown(Lifetime));
}
}

IEnumerator CountDown(float lifetime)
{
yield return m_WaitTime;
ObjectPoolManager.Instance.RemoveGameObject(PoolName, gameObject);
}
}

ObjectInfo 类用于记录对象的信息,并在对象激活时启动协程进行倒计时,当倒计时结束后,将对象从对象池中移除。

4. 对象池管理类 ObjectPoolManager

public class ObjectPoolManager : Singleton<ObjectPoolManager>
{
private Dictionary<string, ObjectPool> m_PoolDic;
private Transform m_RootPoolTrans;

public ObjectPoolManager()
{
m_PoolDic = new Dictionary<string, ObjectPool>();

// 根对象池
GameObject go = new GameObject("ObjectPoolManager");
m_RootPoolTrans = go.transform;
}

// 创建一个新的对象池
public T CreateObjectPool<T>(string poolName) where T : ObjectPool, new()
{
if (m_PoolDic.ContainsKey(poolName))
{
return m_PoolDic[poolName] as T;
}

GameObject obj = new GameObject(poolName);
obj.transform.SetParent(m_RootPoolTrans);
T pool = new T();
pool.Init(poolName, obj.transform);
m_PoolDic.Add(poolName, pool);
return pool;
}

public GameObject GetGameObject(string poolName, Vector3 position, float lifetTime)
{
if (m_PoolDic.ContainsKey(poolName))
{
return m_PoolDic[poolName].Get(position, lifetTime);
}
return null;
}

public void RemoveGameObject(string poolName, GameObject go)
{
if (m_PoolDic.ContainsKey(poolName))
{
m_PoolDic[poolName].Recycle(go);
}
}

// 销毁所有对象池
public void Destroy()
{
m_PoolDic.Clear();
GameObject.Destroy(m_RootPoolTrans);
}
}

ObjectPoolManager 类用于管理多个对象池,提供了创建对象池、获取对象和移除对象等方法,并可以销毁所有对象池。

5. 回收方法的扩展

回收到对象池后可以进行属性重设,不同种类的对象池可以重写 Recycle 方法来重设不同的属性。示例代码如下:

public virtual void Recycle(GameObject obj)
{
// 待分配对象已经在对象池中
if (queue.Contains(obj))
{
Debug.LogWarning("the obj " + obj.name + " be recycle twice!");
return;
}
if (_freeObjCount > preAllocCount + autoIncreaseCount)
{
Destroy(obj); // 当前池中object数量已满,直接销毁
}
else
{
queue.Enqueue(obj); // 入队,并进行reset
obj.transform.parent = this.transform;
obj.SetActive(false);
_freeObjCount++;
}
}

6. 扩展学习

6.1 相关概念

  • ObjectPoolContainer:对象容器
  • ObjectPool:单一对象池
  • PoolManager:对象池管理

6.2 扩展参考项目

UnityPool 是一个可以参考的扩展项目,有助于进一步学习 Unity 对象池的管理。