GameFramework——引用池&对象池

前言

之前做游戏的时候用过对象池,适用于频繁使用回收的对象,可以防止对象反复生成和销毁,减少gc,之前用于人物移动的残影上。
GF中有两个池子,之前一直不知道有什么区别,直到看了传送门,没错又是群主大佬的文章,才知道引用池和对象池原理一样,但是适用的对象不一样,一般引用池用于存储C#类型的对象,对象池用于Unity相关的GameObj对象。

引用池

在这里插入图片描述
打开GF源码,可以看到关于ReferencePool有这四个文件。先分别介绍这四个类和他们的功能,最后拿StarForce中的一个例子来看看~

IReference

在这里插入图片描述
IReference接口,所有要被引用池存储的类型都要继承自该接口,其中有一个Clear方法,会在被放回池子的时候调用,一般用于该引用对象的数据初始化。

ReferenceCollection

我们的游戏中一般有多个引用池,每个引用池存放不同的对象,一个引用池则存放相同类型的对象,一个ReferenceCollection则代表一种类型的引用池
在这里插入图片描述
用一个队列在存储对象。
在这里插入图片描述
其余的属性分别为存放的对象类型,被取出的数量,获取次数,释放次数,加入对象的次数和移除对象的次数。
在这里插入图片描述
在这里插入图片描述

获取对象的两种方法,一种通过泛型,另一种直接获取。
有则取之,无则实例化
在这里插入图片描述
将对象放回池子,调用Clear(),并且检查该对象是否已经被放回防止重复操作。
在这里插入图片描述
Add就直接new并且放入count个对象进队列,remove和removeAll也是类似。

ReferencePool

静态类
在这里插入图片描述
因为外部不能直接拿到引用池,所以是在该类中通过一个字典来存储所有类型引用池,外部就通过它来间接获取
在这里插入图片描述
在这里插入图片描述

强制类型检查,必须该对象是Class,继承自IReference
在这里插入图片描述
可以看到这两个Acquire的区别,分别调用的是ReferenceCollection的Acquire,一个是使用泛型,一个不使用。
使用泛型的就不需要强制类型检查,因为有where约束,如果传入错误的类型直接编译报错非常安全,总的来说用泛型比较好。

ReferenceInfo

引用池信息,结构体类型
在这里插入图片描述
在调用此方法时会返回一个数组,记录所有引用池的信息

使用

这是某个使用引用池的地方,可以看到它继承自IReference,并且实现了Clear()方法。
重点是这个Create()方法,调用Acquire< T >后会创建实例,因为Create传入的参数都不同,所以没有在IReference中写上。
当Release后再次Create就可以直接从引用池中取出而不需要重新创建

//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------

using GameFramework;
using UnityEngine;

namespace UnityGameFramework.Runtime
{
    internal sealed class AttachEntityInfo : IReference
    {
        private Transform m_ParentTransform;
        private object m_UserData;

        public AttachEntityInfo()
        {
            m_ParentTransform = null;
            m_UserData = null;
        }

        public Transform ParentTransform
        {
            get
            {
                return m_ParentTransform;
            }
        }

        public object UserData
        {
            get
            {
                return m_UserData;
            }
        }

        public static AttachEntityInfo Create(Transform parentTransform, object userData)
        {
            //一开始没有就创建一个,放回池子里循环利用
            AttachEntityInfo attachEntityInfo = ReferencePool.Acquire<AttachEntityInfo>();
            attachEntityInfo.m_ParentTransform = parentTransform;
            attachEntityInfo.m_UserData = userData;
            return attachEntityInfo;
        }

        public void Clear()
        {
            m_ParentTransform = null;
            m_UserData = null;
        }
    }
}

对象池

在这里插入图片描述
相比于引用池,对象池更加复杂,如果让我自己写对象池的话能写成引用池那样的已经很满足了。

ObjectBase

对象基类,要通过对象池操作的类需要继承自ObjectBase,ObjectBase本身继承自IReference(内有Clear方法)
此时我们需要考虑,如果是Unity自带的GameObject对象不能继承自ObjectBase怎么办,继续看代码
在这里插入图片描述
其中的m_Target字段才是我们真正需要操作的对象
在这里插入图片描述
可以通过Initialize函数来初始化对象。所以我们如果要用到对象池,则需要重写一个类继承自ObjectBase,内部封装的m_Target才是真正要操作的对象
在这里插入图片描述
其余属性分别为名称,是否加锁,优先级,对象上次使用时间
在这里插入图片描述
子类可能需要重写这两个方法,这两个方法会在Object中Spawn和Unspawn被调用,我们也是通过ObjectPool来获取对象

Object

说实话这个操作我是看懵了,嵌套的有点复杂~
我们先看一下ObjectPool的代码
在这里插入图片描述
上面那个一对多的字典应该就是池子了,然而它存储的是Object< T>类型
在这里插入图片描述

在看到Object,继承自IReference,且T需要继承自ObjectBase
在这里插入图片描述
破案了,这个就是我们存储的对象,准确的是它里面封装的m_Target才是我们真正存储的对象。
Object是ObjectManager的内部类,我们需要通过ObjectManager来拿到它
Object的很多属性和字段和ObjectBase一样,且已经编写了自己这层的Spawn和UnSpawn逻辑,内部调用的ObjectBase的Spawn和Unspawn需要我们自己重写

ObjectPoolBase

接下来我们要看的是ObjectPool
在这里插入图片描述
可以看到它继承自一个抽象类,一个接口,我们先来看看ObjectPoolBase
内容不一一介绍了,大概就是些获取对象类型、释放对象池对象等操作

IObjectPool< T>

和上面的操作差不多,但是多了些针对于泛型的操作,也有Spawn和Unspawn,在其中调用Object< T>的对应函数

区别

为什么要写一个抽象类,一个泛型接口?我一开始也不知道为啥,还是看了群主大佬的文章
因为ObjectPoolBase不是泛型,所以我们在需要获取多个对象池的通用数据时,可以用ObjectPoolBase[]来操作,IObject< T>是明确类型的,保证编译时类型安全,和类型有关的Spawn,Register操作都声明在该接口中。

因为继承自这两个,所以我们在不同的情境下可以以不同的形式来持有对象池
在这里插入图片描述
在这里插入图片描述
这两处就很明显,上面是要进行和类型相关的操作的,下面是ObjectPoolComponentInspector的,是和集合有关的操作

ObjectPool

在这里插入图片描述
两个字典,一个一对多,一个一对一,一对多的应该就是Name对应一个对象集合,一对一的话是实际存储的对象对应一个Object< T>
在这里插入图片描述
从注释我们可以看到每个字段属性的含义
在这里插入图片描述
先调用Object< T>的Spawn,再调用ObjectBase的Onspawn,其中这个Onspawn我们可以重写
在这里插入图片描述
和上面的类似,但是如果数量超出限制,需要调用Release
在这里插入图片描述
次操作和上面的时间、回调都有关系
在这里插入图片描述
筛选可释放对象
最后就是调用我们传入的筛选回调函数(可以自己写)得到一个可释放列表,将其一一释放即可,不详细写了

ObjectPoolManager

在这里插入图片描述
在这里插入图片描述

传入ObjectBase的子类Type和Name作为key,对象池作为Value来获取对应的池子。
还有些创建对象池,释放对象池等操作就不详细说了…StarForce中血条相关的就是具体使用方法,大家可以自己看看~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值