【GameFramework框架内置模块】12、对象池(Object Pool)

推荐阅读

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

【GameFramework框架】系列教程目录:
https://blog.csdn.net/q764424567/article/details/135831551

二、正文

2-1、介绍

对象池(Object Pool)技术,相比大家都了解很多了,使用对象池技术的原因在于对象需要存在频繁的创建和销毁。

为了避免GC,而对对象的创建和销毁进行优化,需要时获取,不需要时回收,而不是销毁。

在GameFramework框架中,用到对象池(Object Pool)共有四处:

  • 1、TaskPool 任务池
  • 2、RefrencePool 引用池
  • 3、 EventPool 事件池
  • 4、ObjectPool 对象池

在GameFramework框架中之所以分成4个对象池去做这些事情,也是为了不同的功能差异而实现的。

2-2、使用说明

我们先看一下如何使用GameFramework的对象池,之后再详细分析四个对象池的实现和使用。

这里以StarForce示例项目为例,StarForce示例项目使用了ObjectPool对象池,适用于Unity预制体对象的对象池使用。

下面,就来看一下如何使用的:

1、HPBarItem.cs

HPBarItem.cs在挂载预制体上,初始化一个使用的血条UI,代码如下:

csharp 复制代码
namespace StarForce
{
    public class HPBarItem : MonoBehaviour
    {
        [SerializeField]
        private Slider m_HPBar = null;
        
        private void Awake();

        //初始化赋值
        public void Init(Entity owner, Canvas parentCanvas, float fromHPRatio, float toHPRatio);

        //回收重置
        public void Reset();
    }
}

挂载HPBarItem.cs预制体其实都可以直接用对象池了,但是为了扩展性,需要一个继承ObjectBase类HPBarItemObject来包装HPBarItem.cs

2、HPBarItemObject.cs

HPBarItemObject.cs的作用很简单,就是让ObjectPool直接管理的是它,它里面包装了HPBarItem

csharp 复制代码
namespace StarForce
{
    public class HPBarItemObject : ObjectBase
    {
    	//HPBarItemObject的创建函数
        public static HPBarItemObject Create(object target)
        {
            HPBarItemObject hpBarItemObject = ReferencePool.Acquire<HPBarItemObject>();
            hpBarItemObject.Initialize(target);
            return hpBarItemObject;
        }
		//对象的释放函数
        protected override void Release(bool isShutdown)
        {
            HPBarItem hpBarItem = (HPBarItem)Target;
            if (hpBarItem == null)
                return;

            Object.Destroy(hpBarItem.gameObject);
        }
    }
}

要实现HPBarItemObject的回收和复用,还需要特定的HPBarComponent类,用来封装回收和复用操作。

3、HPBarComponent.cs

封装HPBarComponent类回收和复用操作。

csharp 复制代码
namespace StarForce
{
    public class HPBarComponent : GameFrameworkComponent
    {
        //复用对象预制体
        private HPBarItem m_HPBarItemTemplate = null;

        //对象父节点
        private Transform m_HPBarInstanceRoot = null;

        //对象池
        private IObjectPool<HPBarItemObject> m_HPBarItemObjectPool = null;

        private void Start()
        {
            //创建对象池
            m_HPBarItemObjectPool = GameEntry.ObjectPool.CreateSingleSpawnObjectPool<HPBarItemObject>("HPBarItem", m_InstancePoolCapacity);
        }

        //创建HPBarItem对象
        public void ShowHPBar(Entity entity, float fromHPRatio, float toHPRatio);

        //回收HpBarItem对象
        private void HideHPBar(HPBarItem hpBarItem);

        //从对象池取对象逻辑(有就复用,没有就创建)
        private HPBarItem CreateHPBarItem(Entity entity)
        {
            HPBarItem hpBarItem = null;
            HPBarItemObject hpBarItemObject = m_HPBarItemObjectPool.Spawn();
            if (hpBarItemObject != null)
            {
                hpBarItem = (HPBarItem)hpBarItemObject.Target;
            }
            else
            {
                hpBarItem = Instantiate(m_HPBarItemTemplate);
                Transform transform = hpBarItem.GetComponent<Transform>();
                transform.SetParent(m_HPBarInstanceRoot);
                transform.localScale = Vector3.one;
                m_HPBarItemObjectPool.Register(HPBarItemObject.Create(hpBarItem), true);
            }

            return hpBarItem;
        }
    }
}

总结一下就是,ObjectPoolManager管理所有继承了ObjectBase类的对象池,HPBarItemObject继承了ObjectBase类,包装了HPBarItem类,使用HPBarComponent去封装HPBarItemObject的回收和复用操作。

这样可拓展性和可维护性非常强,缺点就是对初学者不友好。

2-3、实现及代码分析

2-3-1、TaskPool 对象池

TaskPool.cs

TaskPool是ITaskAgent的集合,负责ITaskAgent的初始化、执行、回收等操作。

csharp 复制代码
/// <summary>
/// 任务池管理器
/// </summary>
internal sealed class TaskPool<T> where T : TaskBase
{
	// 单个任务接口基类
    private readonly GameFrameworkLinkedList<ITaskAgent<T>> m_WorkingAgents;

    //     要增加的任务代理。
    public void AddAgent(ITaskAgent<T> agent){}

    //     根据任务的序列编号获取任务的信息。
    public TaskInfo GetTaskInfo(int serialId){}

    //     移除任务。
    public bool RemoveTask(int serialId){}
}

ITaskAgent.cs

ITaskAgent接口就是单个任务的接口基类,下面是主要的三个子类继承:

csharp 复制代码
1.DownloadAgent:用于服务器文件的下载代理器
2.LoadResourceAgent:用于加载本地AssetBundle资源的加载器
3.WebRequestAgent:web请求代理
2-3-2、RefrencePool 引用池

RefrencePool .cs

引用池管理器,每个ReferenceCollection都是一个引用池,RefrencePool 对每一个ReferenceCollection进行管理。

csharp 复制代码
/// <summary>
/// 任务池管理器
/// </summary>
internal sealed class TaskPool<T> where T : TaskBase
{
	// 单个任务接口基类
    private readonly GameFrameworkLinkedList<ITaskAgent<T>> m_WorkingAgents;

    //     要增加的任务代理。
    public void AddAgent(ITaskAgent<T> agent){}

    //     根据任务的序列编号获取任务的信息。
    public TaskInfo GetTaskInfo(int serialId){}

    //     移除任务。
    public bool RemoveTask(int serialId){}
}

ReferenceCollection.cs

ReferenceCollection是IReference的一个队列集合:

csharp 复制代码
//IReference集合
private sealed class ReferenceCollection
{
    private readonly Queue<IReference> m_References;
}

IReference.cs
IReference接口是所有对先吃,引用的T类型的基类接口,继承IReference接口的子类很多,只要是存在大量的类都可以继承IReference,这些类的特点就是可以Clear清除数据后重用:

csharp 复制代码
PacketHeaderBase:网络消息包头
Event:事件节点
GameFrameworkEventArgs:游戏框架抱哈事件数据的基类
TaskBase:任务基类
Variable:GF变量基类
DataNode:数据节点
EntityInfo:实体信息
Fsm<T>:有限状态机基类
ObjectBase:对象基类
UIFormInfo:界面组信息
LogNode:日志记录节点
WWWFormInfo:www表单信息
2-3-3、EventPool 事件池

EventManager.cs

EventManager维护了EventPool,对象池里面存储了游戏中的事件:

csharp 复制代码
/// <summary>
/// 事件管理器。
/// </summary>
internal sealed class EventManager : GameFrameworkModule, IEventManager
{
    private readonly EventPool<GameEventArgs> m_EventPool;

    /// 订阅事件处理函数。
    public void Subscribe(int id, EventHandler<GameEventArgs> handler);
    
    /// 取消订阅事件处理函数。
    public void Unsubscribe(int id, EventHandler<GameEventArgs> handler);
    
    /// 抛出事件,这个操作是线程安全的,即使不在主线程中抛出,也可保证在主线程中回调事件处理函数,但事件会在抛出后的下一帧分发。
    public void Fire(object sender, GameEventArgs e);
}

EventPool.cs

EventPool事件池负责全局游戏的事件的绑定和发送,EventManager对其进行了封装:

csharp 复制代码
/// <summary>
/// 事件池。
/// </summary>
/// <typeparam name="T">事件类型。</typeparam>
internal sealed partial class EventPool<T> where T : BaseEventArgs
{
    //存储所有(事件,绑定函数)
    private readonly GameFrameworkMultiDictionary<int, EventHandler<T>> m_EventHandlers;
    //多线程安全事件列表,保证线程安全
    private readonly Queue<Event> m_Events;

    /// 订阅事件处理函数。
    public void Subscribe(int id, EventHandler<T> handler);

    /// 取消订阅事件处理函数。
    public void Unsubscribe(int id, EventHandler<T> handler);

    /// 抛出事件,这个操作是线程安全的,即使不在主线程中抛出,也可保证在主线程中回调事件处理函数,但事件会在抛出后的下一帧分发。
    public void Fire(object sender, T e);
    
    /// 处理事件结点。 遍历执行事件回调
    private void HandleEvent(object sender, T e);
    }
}
2-3-4、ObjectPool 对象池

ObjectPool.cs

ObjectPool是针对Object的集合,让我们先来看ObjectPool类的结构:

csharp 复制代码
/// 泛型T的对象池。
private sealed class ObjectPool<T> : ObjectPoolBase, IObjectPool<T> where T : ObjectBase
{
    private readonly GameFrameworkMultiDictionary<string, Object<T>> m_Objects;
    private readonly Dictionary<object, Object<T>> m_ObjectMap;

    /// 创建对象。
    public void Register(T obj, bool spawned);
    
    /// 获取对象。
    public T Spawn();
   
    /// 回收对象。
    public void Unspawn(T obj);
    
    /// 释放对象池中的可释放对象。
    public override void Release();
}

ObjectPool是一个对象类,适用于泛型类Object的获取和回收。

ObjectPool有自动释放机制,在Update中轮询是否有多余的需要释放的对象。

Object.cs

ObjectIReference的一个引用,Object实现了OnSpawnOnUnSpawn事件,方便对象回收和重用时的回调处理。

csharp 复制代码
internal sealed partial class ObjectPoolManager : GameFrameworkModule, IObjectPoolManager
{
    /// 内部对象。
    private sealed class Object<T> : IReference where T : ObjectBase
    {
        private T m_Object;
        private int m_SpawnCount;

        /// 创建内部对象。
        public static Object<T> Create(T obj, bool spawned);

        /// 获取对象。
        public T Spawn();

        /// 回收对象。
        public void Unspawn();

        /// 释放对象。
        public void Release(bool isShutdown);
    }
}

三、后记

如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏 方向 简介
Unity3D开发小游戏 小游戏开发教程 分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶 入门 从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUI UGUI Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据 文件读取 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合 数据集合 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发 虚拟仿真 总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件 插件 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发 日常记录 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG 日常记录 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。
相关推荐
玩电脑的辣条哥2 小时前
Python如何播放本地音乐并在web页面播放
开发语言·前端·python
ll7788115 小时前
LeetCode每日精进:20.有效的括号
c语言·开发语言·算法·leetcode·职场和发展
Jackson@ML6 小时前
Python数据可视化简介
开发语言·python·数据可视化
赵琳琅6 小时前
Java语言的云计算
开发语言·后端·golang
lly2024067 小时前
jQuery 杂项方法
开发语言
赵琳琅7 小时前
MDX语言的安全开发
开发语言·后端·golang
开开又心心的学嵌入式7 小时前
C语言——指针进阶应用
c语言·开发语言
开开又心心的学嵌入式7 小时前
C语言——指针基础知识
c语言·开发语言
lonelyhiker7 小时前
javascript的原型链
开发语言·javascript·原型模式
夏梓蕙8 小时前
Elixir语言的软件开发工具
开发语言·后端·golang