对象池是在游戏开发中的常见优化技术,目的在于避免频繁的创建、销毁对象带来的内存消耗
下面是官网的介绍
打开工程,建立ObjectPool文件夹,然后再建立4个文件
其中
ObjectBase是池对象基类,所有需要被对象池管理的对象都需要继承该类(实际使用的对象被封装在这个类的派生类中,因为需要被对象池管理的对象可能是不同类型的,这样封装方便调用池对象的生命周期)
IObjectPool是对象池接口,ObjectPool是对象池类,使用链表维护指定类型的池对象
ObjectPoolManager是对象池管理器,使用字典维护所有对象池
打开ObjectBase,将其修改为抽象类,添加字段与属性,以及构造方法
/// <summary>
/// 池对象基类
/// </summary>
public abstract class ObjectBase {
/// <summary>
/// 对象名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 对象(实际使用到的对象放到这里)
/// </summary>
public object Target { get; set; }
/// <summary>
/// 对象上次使用时间
/// </summary>
public DateTime LastUseTime { get; private set; }
/// <summary>
/// 对象的获取计数
/// </summary>
public int SpawnCount { get; set; }
/// <summary>
/// 对象是否正在使用
/// </summary>
public bool IsInUse
{
get
{
return SpawnCount > 0;
}
}
public ObjectBase(object target, string name = "")
{
Name = name;
Target = target;
}
}
添加上池对象的生命周期方法
/// <summary>
/// 获取对象时
/// </summary>
protected virtual void OnSpawn()
{
}
/// <summary>
/// 回收对象时
/// </summary>
protected virtual void OnUnspawn()
{
}
/// <summary>
/// 释放对象时
/// </summary>
public abstract void Release();
添加对象的获取与回收方法
/// <summary>
/// 获取对象
/// </summary>
public ObjectBase Spawn()
{
SpawnCount++;
LastUseTime = DateTime.Now;
OnSpawn();
return this;
}
/// <summary>
/// 回收对象
/// </summary>
public void Unspawn()
{
OnUnspawn();
LastUseTime = DateTime.Now;
SpawnCount--;
}
打开IObjectPool,将其修改为接口,并添加一些属性
/// <summary>
/// 对象池接口
/// </summary>
public interface IObjectPool{
/// <summary>
/// 对象池名称
/// </summary>
string Name { get; }
/// <summary>
/// 对象池对象类型
/// </summary>
Type ObjectType { get; }
/// <summary>
/// 对象池中对象的数量。
/// </summary>
int Count { get; }
/// <summary>
/// 对象池中能被释放的对象的数量。
/// </summary>
int CanReleaseCount { get; }
/// <summary>
/// 对象池自动释放可释放对象的间隔秒数(隔几秒进行一次自动释放)
/// </summary>
float AutoReleaseInterval { get; set; }
/// <summary>
/// 对象池的容量。
/// </summary>
int Capacity { get; set; }
/// <summary>
/// 对象池对象过期秒数(被回收几秒钟视为过期,需要被释放)
/// </summary>
float ExpireTime { get; set; }
}
添加释放对象的方法
/// <summary>
/// 释放超出对象池容量的可释放对象
/// </summary>
void Release();
/// <summary>
/// 释放指定数量的可释放对象
/// </summary>
/// <param name="toReleaseCount">尝试释放对象数量。</param>
void Release(int toReleaseCount);
/// <summary>
/// 释放对象池中的所有未使用对象
/// </summary>
void ReleaseAllUnused();
最后添加轮询与关闭的方法
/// <summary>
/// 轮询对象池
/// </summary>
void Update(float elapseSeconds, float realElapseSeconds);
/// <summary>
/// 清理并关闭对象池
/// </summary>
void Shutdown();
然后打开ObjectPool,为其添加泛型(这里的泛型代表这个对象池所管理的池对象的类型)与约束,以及实现IObjectPool接口
/// <summary>
/// 对象池
/// </summary>
public class ObjectPool<T> : IObjectPool where T : ObjectBase
{
}
这时先不急着继续编写,而是在这个类上头定义一个释放对象筛选方法的委托模板
/// <summary>
/// 释放对象筛选方法
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="candidateObjects">要筛选的对象集合</param>
/// <param name="toReleaseCount">需要释放的对象数量</param>
/// <param name="expireTime">对象过期参考时间</param>
/// <returns>经筛选需要释放的对象集合</returns>
public delegate LinkedList<T> ReleaseObjectFilterCallback<T>(LinkedList<T> candidateObjects, int toReleaseCount, DateTime expireTime) where T : ObjectBase;
这个委托等会编写释放对象的方法时需要用到
现在可以回到ObjectPool的编写了,添加一些字段与属性
/// <summary>
/// 对象池容量
/// </summary>
private int m_Capacity;
/// <summary>
/// 对象池对象过期秒数
/// </summary>
private float m_ExpireTime;
/// <summary>
/// 对象池名称
/// </summary>
public string Name { get; private set; }
/// <summary>
/// 池对象的链表
/// </summary>
private LinkedList<ObjectBase> m_Objects;
/// <summary>
/// 池对象类型
/// </summary>
public Type ObjectType
{
get
{
return typeof(T);
}
}
/// <summary>
/// 池对象的数量
/// </summary>
public int Count
{
get
{
return m_Objects.Count;
}
}
/// <summary>
/// 池对象是否可被多次获取
/// </summary>
public bool AllowMultiSpawn { get; private set; }
/// <summary>
/// 可释放的对象的数量
/// </summary>
public int CanReleaseCount
{
get
{
return GetCanReleaseObjects().Count;
}
}
/// <summary>
/// 对象池自动释放可释放对象计时
/// </summary>
public float AutoReleaseTime { get; private set; }
/// <summary>
/// 对象池自动释放可释放对象的间隔秒数
/// </summary>
public float AutoReleaseInterval { get; set; }
/// <summary>
/// 对象池容量
/// </summary>
public int Capacity
{
get
{
return m_Capacity;
}
set
{
if (value < 0)
{
Debug.LogError("设置对象池容量<0,无法设置");
}
if (m_Capacity == value)
{
return;
}
m_Capacity = value;
}
}
/// <summary>
/// 池对象过期秒数
/// </summary>
public float ExpireTime
{
get
{
return m_ExpireTime;
}
set
{
if (value < 0)
{
Debug.LogError("设置对象过期秒数<0,无法设置");
}
if (m_ExpireTime == value)
{
return;
}
m_ExpireTime = value;
}
}
上面这段代码里会有一个地方因为缺少对应方法报错,先不去管它,添加构造方法,在里面进行数据初始化
public ObjectPool(string name, int capacity, float expireTime, bool allowMultiSpawn)
{
Name = name;
m_Objects = new LinkedList<ObjectBase>();
Capacity = capacity;
AutoReleaseInterval = expireTime;
ExpireTime = expireTime;
AutoReleaseTime = 0f;
AllowMultiSpawn = allowMultiSpawn;
}
添加检查对象的方法
/// <summary>
/// 检查对象
/// </summary>
/// <param name="name">对象名称</param>
/// <returns>要检查的对象是否存在</returns>
public bool CanSpawn(string name)
{
foreach (ObjectBase obj in m_Objects)
{
if (obj.Name != name)
{
continue;
}
if (AllowMultiSpawn || !obj.IsInUse)
{
return true;
}
}
return false;
}
添加对象的注册,获取与回收的方法
/// <summary>
/// 注册对象
/// </summary>
/// <param name="obj">对象</param>
/// <param name="spawned">对象是否已被获取</param>
public void Register(T obj, bool spawned = false)
{
if (obj == null)
{
Debug.LogError("要放入对象池的对象为空:" + typeof(T).FullName);
return;
}
//已被获取就让计数+1
if (spawned)
{
obj.SpawnCount++;
}
m_Objects.AddLast(obj);
}
/// <summary>
/// 获取对象
/// </summary>
/// <param name="name">对象名称</param>
/// <returns>要获取的对象</returns>
public T Spawn(string name = "")
{
foreach (ObjectBase obj in m_Objects)
{
if (obj.Name != name)
{
continue;
}
if (AllowMultiSpawn || !obj.IsInUse)
{
Debug.Log("获取了对象:" + typeof(T).FullName + "/" + obj.Name);
return obj.Spawn() as T;
}
}
return null;
}
/// <summary>
/// 回收对象
/// </summary>
public void Unspawn(ObjectBase obj)
{
Unspawn(obj.Target);
}
/// <summary>
/// 回收对象
/// </summary>
public void Unspawn(object target)
{
if (target == null)
{
Debug.LogError("要回收的对象为空:" + typeof(object).FullName);
}
foreach (ObjectBase obj in m_Objects)
{
if (obj.Target == target)
{
obj.Unspawn();
Debug.Log("对象被回收了:" + typeof(T).FullName + "/" + obj.Name);
return;
}
}
Debug.LogError("找不到要回收的对象:" + typeof(object).FullName);
}
添加获取可释放对象的方法(刚才报错时缺少的对应方法)
/// <summary>
/// 获取所有可以释放的对象
/// </summary>
private LinkedList<T> GetCanReleaseObjects()
{
LinkedList<T> canReleaseObjects = new LinkedList<T>();
foreach (ObjectBase obj in m_Objects)
{
if (obj.IsInUse)
{
continue;
}
canReleaseObjects.AddLast(obj as T);
}
return canReleaseObjects;
}
添加释放对象的主要方法
/// <summary>
/// 释放对象池中的可释放对象
/// </summary>
/// <param name="toReleaseCount">尝试释放对象数量</param>
/// <param name="releaseObjectFilterCallback">释放对象筛选方法</param>
public void Release(int toReleaseCount, ReleaseObjectFilterCallback<T> releaseObjectFilterCallback)
{
//重置计时
AutoReleaseTime = 0;
if (toReleaseCount <= 0)
{
return;
}
//计算对象过期参考时间
DateTime expireTime = DateTime.MinValue;
if (m_ExpireTime < float.MaxValue)
{
//当前时间 - 过期秒数 = 过期参考时间
expireTime = DateTime.Now.AddSeconds(-m_ExpireTime);
}
//获取可释放的对象和实际要释放的对象
LinkedList<T> canReleaseObjects = GetCanReleaseObjects();
LinkedList<T> toReleaseObjects = releaseObjectFilterCallback(canReleaseObjects, toReleaseCount, expireTime);
if (toReleaseObjects == null || toReleaseObjects.Count <= 0)
{
return;
}
//遍历实际要释放的对象
foreach (ObjectBase toReleaseObject in toReleaseObjects)
{
if (toReleaseObject == null)
{
Debug.LogError("无法释放空对象");
}
foreach (ObjectBase obj in m_Objects)
{
if (obj != toReleaseObject)
{
continue;
}
//释放对象
m_Objects.Remove(obj);
obj.Release();
Debug.Log("对象被释放了:" + obj.Name);
break;
}
}
}
添加默认的筛选方法
/// <summary>
/// 默认的释放对象筛选方法(未被使用且过期的对象)
/// </summary>
private LinkedList<T> DefaultReleaseObjectFilterCallBack(LinkedList<T> candidateObjects, int toReleaseCount, DateTime expireTime)
{
LinkedList<T> toReleaseObjects = new LinkedList<T>();
if (expireTime > DateTime.MinValue)
{
LinkedListNode<T> current = candidateObjects.First;
while (current != null)
{
//对象最后使用时间 <= 过期参考时间,就需要释放
if (current.Value.LastUseTime <= expireTime)
{
toReleaseObjects.AddLast(current.Value);
LinkedListNode<T> next = current.Next;
candidateObjects.Remove(current);
toReleaseCount--;
if (toReleaseCount <= 0)
{
return toReleaseObjects;
}
current = next;
continue;
}
current = current.Next;
}
}
return toReleaseObjects;
}
主方法编写完成后,添加3个释放对象的相关方法
/// <summary>
/// 释放超出对象池容量的可释放对象
/// </summary>
public void Release()
{
Release(m_Objects.Count - m_Capacity, DefaultReleaseObjectFilterCallBack);
}
/// <summary>
/// 释放指定数量的可释放对象
/// </summary>
/// <param name="toReleaseCount"></param>
public void Release(int toReleaseCount)
{
Release(toReleaseCount, DefaultReleaseObjectFilterCallBack);
}
/// <summary>
/// 释放对象池中所有未使用对象
/// </summary>
public void ReleaseAllUnused()
{
LinkedListNode<ObjectBase> current = m_Objects.First;
while (current != null)
{
if (current.Value.IsInUse)
{
current = current.Next;
continue;
}
LinkedListNode<ObjectBase> next = current.Next;
m_Objects.Remove(current);
current.Value.Release();
Debug.Log("对象被释放了:" + current.Value.Name);
current = next;
}
}
最后在添加轮询与关闭的方法,这个类就编写完成了
/// <summary>
/// 对象池的定时释放
/// </summary>
public void Update(float elapseSeconds, float realElapseSeconds)
{
AutoReleaseTime += realElapseSeconds;
if (AutoReleaseTime < AutoReleaseInterval)
{
return;
}
Release();
}
/// <summary>
/// 清理对象池
/// </summary>
public void Shutdown()
{
LinkedListNode<ObjectBase> current = m_Objects.First;
while (current != null)
{
LinkedListNode<ObjectBase> next = current.Next;
m_Objects.Remove(current);
current.Value.Release();
Debug.Log("对象被释放了:" + current.Value.Name);
current = next;
}
}
现在打开ObjectPoolManager,使其继承ManagerBase,添加字段与属性,并在构造方法中初始化
public class ObjectPoolManager : ManagerBase{
/// <summary>
/// 默认对象池容量
/// </summary>
private const int DefaultCapacity = int.MaxValue;
/// <summary>
/// 默认对象过期秒数
/// </summary>
private const float DefaultExpireTime = float.MaxValue;
/// <summary>
/// 对象池字典
/// </summary>
private Dictionary<string, IObjectPool> m_ObjectPools;
public override int Priority
{
get
{
return 90;
}
}
/// <summary>
/// 对象池数量
/// </summary>
public int Count
{
get
{
return m_ObjectPools.Count;
}
}
public ObjectPoolManager()
{
m_ObjectPools = new Dictionary<string, IObjectPool>();
}
}
添加Manager的生命周期方法
public override void Init()
{
}
public override void Shutdown()
{
foreach (IObjectPool objectPool in m_ObjectPools.Values)
{
objectPool.Shutdown();
}
m_ObjectPools.Clear();
}
public override void Update(float elapseSeconds, float realElapseSeconds)
{
foreach (IObjectPool objectPool in m_ObjectPools.Values)
{
objectPool.Update(elapseSeconds, realElapseSeconds);
}
}
添加检查对象池的方法
/// <summary>
/// 检查对象池
/// </summary>
public bool HasObjectPool<T>() where T : ObjectBase
{
return m_ObjectPools.ContainsKey(typeof(T).FullName);
}
添加对象池的创建、获取与销毁的方法
/// <summary>
/// 创建对象池
/// </summary>
public ObjectPool<T> CreateObjectPool<T>(int capacity = DefaultCapacity, float exprireTime = DefaultExpireTime, bool allowMultiSpawn = false) where T : ObjectBase
{
string name = typeof(T).FullName;
if (HasObjectPool<T>())
{
Debug.LogError("要创建的对象池已存在");
return null;
}
ObjectPool<T> objectPool = new ObjectPool<T>(name, capacity, exprireTime, allowMultiSpawn);
m_ObjectPools.Add(name, objectPool);
return objectPool;
}
/// <summary>
/// 获取对象池
/// </summary>
public ObjectPool<T> GetObjectPool<T>() where T : ObjectBase
{
IObjectPool objectPool = null;
m_ObjectPools.TryGetValue(typeof(T).FullName, out objectPool);
return objectPool as ObjectPool<T>;
}
/// <summary>
/// 销毁对象池
/// </summary>
public bool DestroyObjectPool<T>()
{
IObjectPool objectPool = null;
if (m_ObjectPools.TryGetValue(typeof(T).FullName, out objectPool))
{
objectPool.Shutdown();
return m_ObjectPools.Remove(typeof(T).FullName);
}
return false;
}
最后添加释放对象池对象的方法
/// <summary>
/// 释放所有对象池中的可释放对象。
/// </summary>
public void Release()
{
foreach (IObjectPool objectPool in m_ObjectPools.Values)
{
objectPool.Release();
}
}
/// <summary>
/// 释放所有对象池中的未使用对象。
/// </summary>
public void ReleaseAllUnused()
{
foreach (IObjectPool objectPool in m_ObjectPools.Values)
{
objectPool.ReleaseAllUnused();
}
}
到这里整个ObjectPool模块已经编写完成了,该开始测试了
按照惯例,建立测试需要的文件夹,场景,空物体和脚本
打开TestObject,使其继承ObjectBase
public class TestObject : ObjectBase
{
public TestObject(object target, string name = "") : base(target, name)
{
}
public override void Release()
{
}
}
然后打开测试脚本,编写测试代码
public class ObjectPoolTestMain : MonoBehaviour {
private ObjectPool<TestObject> m_testPool;
private void Start()
{
//创建对象池
ObjectPoolManager m_objectPoolManager = FrameworkEntry.Instance.GetManager<ObjectPoolManager>();
m_testPool = m_objectPoolManager.CreateObjectPool<TestObject>();
//注册对象
TestObject testObject = new TestObject("hello ObjectPool","test1");
m_testPool.Register(testObject, false);
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
TestObject testObject = m_testPool.Spawn("test1");
Debug.Log(testObject.Target);
m_testPool.Unspawn(testObject.Target);
}
}
}
启动游戏,点击鼠标左键,测试成功