简易版GameFramework游戏框架搭建教程(七)ObjectPool

对象池是在游戏开发中的常见优化技术,目的在于避免频繁的创建、销毁对象带来的内存消耗

下面是官网的介绍



打开工程,建立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);
        }
    }
}

启动游戏,点击鼠标左键,测试成功


猜你喜欢

转载自blog.csdn.net/qq_32821435/article/details/80554865