【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和坑,让后来人可以有些参考。
相关推荐
海威的技术博客17 分钟前
JS中的原型与原型链
开发语言·javascript·原型模式
WPG大大通24 分钟前
基于DIODES AP43781+PI3USB31531+PI3DPX1207C的USB-C PD& Video 之全功能显示器连接端口方案
c语言·开发语言·计算机外设·开发板·电源·大大通
从以前38 分钟前
【算法题解】Bindian 山丘信号问题(E. Bindian Signaling)
开发语言·python·算法
high20111 小时前
【Java 基础】-- ArrayList 和 Linkedlist
java·开发语言
1nullptr1 小时前
lua和C API库一些记录
开发语言·lua
Jerry Nan1 小时前
Lua元表
开发语言·lua
?333331 小时前
CTFHub Web进阶-PHP-Bypass disable_function攻略
开发语言·安全·web安全·php
所以经济危机就是没有新技术拉动增长了1 小时前
二、javascript的进阶知识
开发语言·javascript·ecmascript
Bubluu1 小时前
浏览器点击视频裁剪当前帧,然后粘贴到页面
开发语言·javascript·音视频