Unity Game FrameWork—模块使用—对象池分析

news2024/10/5 20:22:37

官方说明:提供对象缓存池的功能,避免频繁地创建和销毁各种游戏对象,提高游戏性能。除了 Game Framework 自身使用了对象池,用户还可以很方便地创建和管理自己的对象池。
下图是Demo中用到的对象池,所有的实体以及UI都使用了对象池。
[图片]

Domo中已经有了实体Entity对象池,可以满足存储GameObject的需求,这里仅能满足我们的实体使用需求,当我们需要添加新的对象池而非依赖于实体时,由于对原理不了解,导致无从下手。下面我们通过实体对象池来分析一下框架的原理和思路。

实体对象池引用池联系

如图,实体对象上挂在了两个脚本,Entity和Asteroid。这两个脚本并没有继承对象基类ObjectBase,而ObjectBase则是实现实体对象池必须继承的。下面我们先理一下实体的相关逻辑,理清分支。
[图片]

实体管理器里的实体信息类EntityInfo继承了IReference
[图片]

实体信息类的创建利用了引用池,却没有用到对象池
[图片]

m_EntityInfos字典存储了实体的ID以及实体信息
InternalShowEntity方法将实体编号以及实体信息填入了m_EntityInfos字典。即,每次显示实体都会填入新的实体信息。并且每次实体信息填入前,实体组件的序号m_Serial对应+1。
[图片]

[图片]

实体对象(池)继承关系

1、EntityInfo : IReference
实体信息类继承于IReference,是实体管理器EntityManager的私有密封类,实体信息类由引用池创建和释放,用于辅助管理实体。
实体信息类内部属性:
m_Status:实体状态用于防止状态切换出错,如我们调用了影藏实体A,而另外一个地方却在给实体A添加子实体B吗,此时程序内会报错,以避免出现不可控的现象。当我们连续两次调用影藏实体时,若第一次已经影藏,则第二次不再生效。相当于状态锁,处于过渡状态时,避免再次调用。
m_Entity、m_ParentEntity、m_ChildEntities:存储了实体的引用,以及父实体、子实体列表的引用。
实体信息类用途:
m_EntityInfos实体信息存储字典
字典存储了实体的编号以及实体信息类,编号从实体的数据表获取。实体管理器EntityManager显示实体是,将实体信息填入m_EntityInfos,隐藏实体时,从m_EntityInfos里移除。
m_RecycleQueue实体信息回收队列
m_RecycleQueue的作用是配合m_EntityInfos释放实体,每一帧会检测回收队列是否有待回收的实体,如果有,则释放队列里的第一个实体。缓慢释放避免了集中释放造成的卡顿。
实体信息类如何管理实体:
显示实体的方法InternalShowEntity里面可以看到,实体辅助器DefaultEntityHelper.CreateEntity方法给加载出来的游戏对象添加了Entity(UnityGameFramework.Runtime)组件,并返回IEntity。IEntity作为参数用于从引用池创建实体信息类。随着实体信息类entityInfo.Status的状态切换,Entity完成了实体初始化(下面会说),实体加入实体组,以及实体显示。
实体影藏也是如此,在InternalHideEntity方法中,随着状态切换完成解除实体,影藏实体,从实体组中移除实体,随后将实体信息类加入到回收队列。

2、Entity : EntityLogic : MonoBehaviour(实体业务逻辑组件)
Entity继承于实体逻辑基类EntityLogic,EntityLogic作为抽象类定义了实体逻辑生命周期的相关方法。
具体的每一个实体则继承于Entity。实体在其业务逻辑的适当时机触发实体的生命周期方法。Asteroid、Aircraft等其他实体的业务逻辑类均继承与EntityLogic。
至此,Entity的实体业务逻辑并不完整,Entity实现后也只是在其中增加了业务逻辑,例如当飞行器掉血死亡时,实体影藏的生命周期方法里只是实现了影藏时触发特效等逻辑内容。并没有对其调用,也没有涉及实体对象池,而继承的EntityLogic中定义的也只是虚方法,并没有对象池的实现。继续往下:

3、Entity : MonoBehaviour, IEntity(实体生命周期组件)
Entity 实体完成了实体生命周期的回收、显示、轮询、附加解除子实体等调用。命名空间为:UnityGameFramework.Runtime。
Entity的私有成员包含EntityLogic成员,即实体业务逻辑组件。Entity生命周期方法主要是触发继承于EntityLogic的实体类的生命周期的相关业务逻辑。
其本身并没有花哨的功能实现,只是在生命周期方法里调用了EntityLogic的生命周期业务逻辑。它存在的意义是什么?注意:在上文的实体信息类如何管理实体的部分,实体管理器EntityManager的InternalShowEntity方法给游戏对象添加了该组件,并触发了初始化、加入实体组、实体显示的功能。在InternalHideEntity方法触发了实体解绑、影藏的逻辑。
Entity的作用便体现出来了它沟通了实体管理器EntityManager与实体业务逻辑之间的联系EntityLogic使生命周期的框架代码与生命周期的业务逻辑代码分割开,对框架做了一层封装只对外暴露了生命周期的接口。外部调用生命周期方法时,通过管理器来实现间接调用。

对象池使用流程分析

最开始的一张图里面我们可以看到实体是通过对象池完成创建回收的,可是分析到这,丝毫没有涉及到对象池,反倒多了一个实体信息类EntityInfo的引用池用来管理实体信息。从下图可以看到实体相关的引用池有AttachEntityInfo、EntityInfo、EntityInstanceObject、ShowEntityInfo。本着对象池是对引用池的一种封装的原则,我们从实体相关的引用池入手。
[图片]

我们通过观察这几个类,只有EntityInstanceObject继承于ObjectBase,其他都是继承于IReference。可以肯定,对象池是的内容是基于EntityInstanceObject开始的。其他的实体相关引用,有兴趣可自行学习,这里主要看下EntityInstanceObject是如何实现实体对象池的。
最终结束的地方是,对象池的信息是怎么显示到对象池组件的检视器面板上的。
在对象池组件检视器面板修饰类ObjectPoolComponentInspector里面可以找到,检视器面板显示的是对象池基类数组ObjectPoolBase[]所存储的信息。开头(EntityInstanceObject)和结尾(ObjectPoolBase[])被找到了,剩下就是怎么从开头到结尾的,把整个过程联系起来。
ObjectPoolBase[]是通过ObjectPoolComponent.GetAllObjectPools()方法获取的,来源则是对象池管理器ObjectPoolManager的m_ObjectPools对象池字典。对象池的创建、销毁调用的是InternalCreateObjectPool()和InternalDestroyObjectPool()方法。而实体对象池的创建必然是通过实体管理器来调用对象池管理器的InternalCreateObjectPool()方法创建对象池,继续顺着这个思路往下找,ObjectPoolManager里面有三处调用了该方法,分别是406行创建UI对象池,699行创建实体对象池,774行创建资源对象池。
实体对象池的创建是在实体组EntityManager.EntityGroup初始化时调用了
objectPoolManager.CreateSingleSpawnObjectPool()方法,并将实体组的信息传入完成创建。
[图片]

实体对象池的信息存储在实体组的对象池m_InstancePool里面。m_InstancePool则是实现了IObjectPool接口的对象池ObjectPool。ObjectPool里面实现了初始化对象池的新实例、创建对象、回收对象、释放对象等方法,增加了很多对象池功能。
ObjectPool的m_Objects存储了对象名及对象类的键值对,m_ObjectMap存储了对象本体及对象类的键值对。
我们看一下对象池ObjectPool里是在哪创建(注册)对象的,通过溯源可以找到EntityManager.ShowEntity()方法,用户调用该方法显示实体,在显示之前首先完成加载m_ResourceManager.LoadAsset(),在加载成功的回调事件中EntityManager.LoadAssetSuccessCallback()中调用EntityInstanceObject.Create()创建对象实例,create方法里面完成了从引用池获取引用。实例对象创建完成后,调用对应实体组的RegisterEntityInstanceObject方法,RegisterEntityInstanceObject调用了ObjectPool的Register方法,最终将对象实例添加进了对象池。
从对象池中取出的过程,有兴趣的话,可以走一遍流程,分析一下。
从整个过程可以看出实体对象池是依赖于实体组存在的,实体组中创建了对应实体的对象(EntityInstanceObject)。当对象被创建时,首先被添加到引用池,随后再添加到实体组的对象池中。实体对象池中对象的释放,则是按照实体组定义的间隔秒数来执行,先从对象池中释放,再从引用池中释放。
Demo实现了实体对象池、UI对象池以及资源对象池,后续我们实现自己的对象池,加深对对象池的使用和理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/416520.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot集成ChatGPT实现AI聊天

前言 ChatGPT已经组件放开了,现在都可以基于它写插件了。但是说实话我还真没想到可以用它干嘛,也许可以用它结合文字语音开发一个老人小孩需要的智能的说话陪伴啥的。 今天我就先分享下SpringBoot结合ChatGPT,先看看对话效果。 一、依…

清明-前端

明天面快手前端,正好借这个机会,做做毕设吧。顺便整理一下前端的面试内容。 何良蓉说,他觉得学的时候开心,玩的时候也开心。我觉得不开心。可能他掌握生活的秘密了吧。 如果他没对我撒谎的话。 看了眼别人的面经,就知…

一键禁用系统防火墙

你也可以通过批处理命令来实现 桌面空白地方右键选择新建记事本将下面代码复制到记事本里,然后保存为.bat类型的文件;保存完成运行即可。 Echo off Echo -------------------------------------------------------------------------- Echo 禁用系统防火…

【CSS】定位 ② ( 静态定位 | 相对定位 )

文章目录一、静态定位二、相对定位1、标准流下的盒子模型代码示例2、相对定位下的盒子模型代码示例一、静态定位 CSS 中的 静态定位 是 默认的定位方式 , 就是无定位 , 设置该定位方式 , 定位盒子不生效 ; 为盒子模型 设置 静态定位 模式 , 该 盒子模型 就会按照标准流的方式 …

【面试】spring中怎么解决循环依赖问题?

文章目录前言1、什么是循环依赖?2、Spring怎么解决循环依赖3、如何解决?4、怎么样的循环依赖无法处理?5、总结:前言 思考: 什么是循环依赖?Spring怎么解决循环依赖Spring对于循环依赖无法解决的场景 1、什么是循环依赖? 循环…

Run Loops

Run Loops 运行循环是与线程相关的基本基础结构的一部分。运行循环是事件处理循环,用于安排工作并协调传入事件的接收。运行循环的目的是在有工作要做时让线程保持忙碌,在没有工作要做时让线程休眠。 运行循环管理不是完全自动的。您仍然必须设计线程代…

【Java Web】012 -- SpringBootWeb综合案例(登录功能、登录校验、异常处理)

目录 一、登录功能 1、基础登录功能 ①、SQL语句 ②、接口参数 ③、实现思路 ④、实现步骤 2、联调Bug(没有Cookie或Session) 二、登录校验 1、登录校验的实现思路 2、会话技术 ①、会话与会话跟踪 ②、会话跟踪方案对比 Cookie Session …

C# 多线程编程

1 线程与进程 进程:进程可以理解为一块包含了某些些资源的内存区域,操作系统通过进程这一方式把他的工作划分为不同的单元。一个应用程序可以对应于多个进程。 线程:线程是进程中的独立执行单元, 对于操作系统而言,他…

【学习笔记】unity脚本学习(四)【inputManager、键盘输入、鼠标输入、Raycast】

目录输入inputManagerHorizontal虚拟轴的各个属性含义(摘选自ChatGpt,部分回答不准确)键值的含义键名称命名约定:键盘输入静态函数GetKeyGetButtonKeyCodeGetButton/Down/upGetAxisGetAxisRaw 返回由 axisName 标识的虚拟轴的值&a…

【Gradle-2】一文搞懂Gradle配置

1、前言 “Gradle的配置太多了,经常版本更新还有变化,而且它还能扩展,记是记不住了,只能用到再搜了,哎,难顶” 真的难顶,但是我想挑战一下… 本文介绍的重点: Gradle配置简介Grad…

slam与导航开发

Gmapping是一种基于激光雷达数据的地图构建算法,可以用于建立机器人的环境地图。 Gmapping算法是基于粒子滤波器(Particle Filter)的SLAM算法,它通过将机器人在环境中的位姿和地图中的特征进行联合估计,实现了机器人在…

个人使用mac OS和win OS的差异

苹果 macOS 操作系统和 Windows 操作系统在很多方面有所不同,主要体现在以下几个方面: 用户界面:macOS 和 Windows 的用户界面风格不同。macOS 推崇简洁、优雅的设计,注重操作体验;Windows 软件更为丰富,但…

Doris(1):Doris介绍

1 Doris简介 Apache Doris是一个现代化的基于MPP(大规模并行处理)技术的分析型数据库产品。简单来说,MPP是将任务并行的分散到多个服务器和节点上,在每个节点上计算完成后,将各自部分的结果汇总在一起得到最终的结果(…

最小生成树和最短路径及其他

还是学过的,主要用于复习q v q 一、最小生成树 最小生成树的定义 用于无向图中,无向图指的是没有带方向路径的图,给定n个点,m条边,如果将这些点依次相连,求出连接这些点的最小数值 应用场景 根据这个算…

9 个JSON.stringify的秘密大多数开发人员却不知道

作为前端开发工程师,你一定用过JSON.stringify,但你知道它的全部秘密吗? 很久以前,我因此在工作中犯下了无法挽回的错误。如果我早点知道,就不会发生这样的悲剧。 理解 JSON.stringify 基本上,JSON.stri…

神经网络辐射场NeRF、实时NeRF Baking、有向距离场SDF、占用网络Occupancy、NeRF 自动驾驶

1 NeRF原理2 NeRF加速PlenoxelsKiloNeRFInstant NGPTensoRF3 SDF NeRF4 Occupancy NeRF5 NeRF应用简介常见应用实际应用的挑战6 NeRF自动驾驶1 NeRF原理 NeRF (Neural Radiance Fields,神经辐射场) 是2020年ECCV会议上的Best Paper,其将隐式表达推上了…

安卓玩机搞机----移植第三方rom修复 第三方GSI系统修复bug综合解析【一】

很多朋友热衷与刷写第三方非当前机型官方系统的rom。和刷写第三方gsi等等。例如 米系列机型刷写Flyme 一加机型刷写miui oppo刷写gsi等等。 很多友友也会尝试自己移植第三方rom。但此类操作最大的问题在于修复可以开机后的bug,今天的教程综合说明下这类修复思路…

STM32F103RCT6

STM32F103RCT6是一款由STMicroelectronics公司生产的基于ARM Cortex-M3内核的32位微控制器。 它具有高性能、低功耗和广泛的应用领域。 包括ADC(模数转换器) DAC(数字模拟转换器) TIM(定时器) USART&#x…

Python 中 SyntaxError: ‘yield‘ outside function 错误

当我们在函数外部使用 yield 关键字时,会出现 Python “SyntaxError: ‘yield’ outside function”。 要解决该错误,如果我们需要对每个元素执行一些运算符,请使用列表理解,或者缩进函数内部使用 yield 的代码。 下面是一个产生…

Maven(四):Maven的使用(中)

Maven(四):Maven的使用(中)前言一、实验四:创建 Maven 版的 Web 工程1、说明2、操作3、生成的pom.xml4、生成的Web工程的目录结构5、创建 Servlet5.1 在 main 目录下创建 java 目录5.2 在 java 目录下创建 …