Unity实现简单易用的对象池
原理
当场景中需要频繁实例化一种对象时,由于实例化的过程会比较耗时,所以可以事先创建一堆对象,然后在需要时激活它。
池子中,我们有两个列表,一个是正在被使用的对象,另一个是还没有被使用的对象,需要一个物体时,从正在使用的列表中取出一个物体,然后将它标记为“已被使用”,其实就是放到“已被使用的”列表中。当物体被使用完,归还物体到池中时,进行相反的操作。
##源码
public static class ObjectPool
{
private class ObjectBuffer<T> where T : MonoBehaviour
{
public T prefab = null;
public int initSize = 0;
public Action<T> onCreate = null;
public Action<T> onPop = null;
public Action<T> onPush = null;
public List<T> used = new List<T>();
public List<T> unused = new List<T>();
public void Grow()
{
T obj;
for (int i = 0; i < initSize; ++i)
{
obj = GameObject.Instantiate<T>(prefab);
onCreate?.Invoke(obj);
unused.Add(obj);
}
}
public T Pop()
{
if (unused.Count <= 0)
Grow();
T obj = unused[0];
unused.RemoveAt(0);
used.Add(obj);
onPop?.Invoke(obj);
return obj;
}
public void Push( T obj )
{
if (used.Contains(obj))
{
used.Remove(obj);
unused.Add(obj);
onPush?.Invoke(obj);
}
}
}
private static Dictionary<Type, object> m_buffers = new Dictionary<Type, object>();
public static void CreatePool<T>( T prefab, Action<T> onCreate = null, Action<T> onPop = null, Action<T> onPush = null, int count = 64 ) where T : MonoBehaviour
{
ObjectBuffer<T> buffer = new ObjectBuffer<T>()
{
prefab = prefab,
initSize = count,
onCreate = onCreate,
onPop = onPop,
onPush = onPush
};
buffer.Grow();
m_buffers.Add(typeof(T), buffer);
}
public static T PopObject<T>() where T : MonoBehaviour
{
if(m_buffers.ContainsKey(typeof(T)))
{
if (m_buffers[typeof(T)] is ObjectBuffer<T> buffer)
return buffer.Pop();
}
return null;
}
public static void PushObject<T>( T obj) where T : MonoBehaviour
{
if(m_buffers.ContainsKey(typeof(T)))
{
if (m_buffers[typeof(T)] is ObjectBuffer<T> buffer)
buffer.Push(obj);
}
}
}
例子:
子弹类
public class Bullet : MonoBehaviour
{
private float life = 0;
void Update()
{
life += Time.deltaTime;
if( life > 5 )
{
ObjectPool.PushObject<Bullet>(this);
life = 0;
}
transform.Translate(transform.forward * 0.5f);
}
}
测试类,按下鼠标发射子弹
public class Test : MonoBehaviour
{
[SerializeField]
private Bullet m_prefab = null;
private void Awake()
{
ObjectPool.CreatePool<Bullet>(m_prefab, OnCreateOrPush,
x => {
x.gameObject.SetActive(true); }, OnCreateOrPush);
}
private void OnCreateOrPush( Bullet obj)
{
obj.gameObject.SetActive(false);
obj.transform.position = transform.position;
obj.transform.rotation = transform.rotation;
}
private void Update()
{
if(Input.GetMouseButton(0))
{
ObjectPool.PopObject<Bullet>();
}
}
}