【Unity学习笔记】对象池

news2024/9/24 19:23:33

文章目录

  • 设计思路
    • 总体设计
    • 从生命周期考虑
  • 一些代码


对象池这个东西老生常谈了,使用它的好处在于:当我们需要重复创建或者销毁一些物体,例如限制子弹数量上限为10发,当射出第11发就需要使第10发消失,第11出现。销毁10号子弹和创建11号子弹虽然能实现,但是由于创建和销毁比较消耗性能,因此不应该这样实现。如果使用对象池技术就可以避免创建销毁,改为隐藏和显示。也就是显示11号,隐藏10号子弹实现一样的功能。通过对象池,如果要发射1000发子弹,我们不必实现1000次创建和销毁,只需11次创建和销毁(产生11个子弹并依次显示即可了。

设计思路

在设计对象池的时候,我们会考虑到使用设计模式的问题。像吃鸡游戏里有口径不同的子弹,那么不同枪也有不同的弹容,不同的射速也会造成同一时间内不同枪支的对象池中可以同时存在的子弹数量不同,也就意味着不同的枪有不同的对象池,且不同的对象池使用的子弹,子弹的最大容量都有不同。在之前的笔记中学习到了Monobehavior的生命周期,每个生命周期都是要在每帧消耗性能的。因此我们要仔细考虑整个对象池的设计模式:

应当怎样实现对象池?首先从设计需求来看,一个对象池中存放N发子弹(N应当是可变的),且对象池中子弹也不能固定。其次,如果在游戏中切换枪支,由于不同枪支各有自己的对象池,因此使用的对象池也应当改变。最后,射击游戏中枪支的使用一般是贯穿全程的,所以无论场景变换,对象池始终应该是DDOL的。


总体设计

基于上述的想法,我们可以确定一个设计模式:首先用一个对象池管理器,这个对象池管理器是DDOL的,并且它可以管理对象池的切换,相当于切枪时也要切换对应的对象池。对于这个功能,我们可以用字典来实现,通过键值对以不同的键值来对应不同的对象池。对象池管理器可以方便的进行对象池(枪支)管理,例如有的枪支子弹用完了就要丢弃,我们就卸载对应的对象池,捡到新枪就加载对应的对象池。由于它是DDOL的,我们可以把上个场景的枪也保留到下一个场景,而不是销毁后重新加载。而基于这样的性质,对象池在游戏中也应当是全局唯一的,这就要求我们为它继承一个单例模式。

其次,对象池的设计,一般而言,我们应当在Awake时就为对象池生成最大数量N的子弹,而不是需要时生成,因为生成子弹的卡顿可能会摧毁玩家的游戏体验。我们宁可把1s的生成的时间放在加载界面,也不能让玩家在游玩时有0.1秒的卡顿。对于子弹在对象池中的存取,先发射的肯定先消失,那么队列就是最适合的数据结构,我们将其出队,当射程结束后再入队。这样就能保证持续射击时能不断生成子弹。

最后是子弹,由于同一个对象池类应当能发射不同的子弹,我们可以把子弹作为预制件来保存。有些属性是可以挂载在子弹物体本身上的。例如设计游戏时枪支属性和子弹属性分别计算的时候。枪支可以设计基类,那我们也可以设计一个子弹的基类来让其余子弹继承。


从生命周期考虑

决定这些系统的设计模式之后,那么应当考虑哪些脚本需要继承Monobehavior的生命周期。首先子弹是绝对不能继承的,如果对象池最多有100发子弹,并且同时拥有10个对象池。如果子弹有生命周期就会有1000个生命周期,反之则只有10个。所以子弹不能有生命周期,因此对于子弹的运动,我们应当把子弹的功能设计统筹到它的对象池中去,在对象池中用Update或者协程为这个GameObject进行一些需要更新的处理。

其次就是对象池,这个对象应该继承Mono behavior,这使得不同的对象池能够独立的处理它们自身不同的情况,更好的设计是写一个对象池的父类,并让子类来继承并重写一些可自定义的方法。

最后就是对象池管理器,如果我们想要它DDOL,那就需要继承Mono;如果它不用和生命周期交互,只是简单的管理对象池,就不需要,可继承也可不继承。而通常我们都是在单例模式设计时设计该单例是否继承Mono的。


一些代码

单例模式

public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;
    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<T>();
            }
            if (_instance == null)
            {
                GameObject obj = new GameObject();
                obj.name = typeof(T).Name;
                obj.AddComponent<T>();
            }
            return _instance;
        }
    }
}

继承单例的对象池管理器

public class ObjPoolManager : Singleton<ObjPoolManager>
{
    [SerializeField]
    public Dictionary<string, ObjPool> m_poolDic;
    /// <summary>
    /// 读取挂载的子物体中的对象池并加入字典
    /// </summary>
    /// <param name="obj">用于统一管理对象池的父物体</param>
    public void initPool(GameObject obj)
    {
        m_poolDic = new Dictionary<string, ObjPool>();
        for (int i = 0; i < obj.transform.childCount; i++)
        {
            var thisChild = obj.transform.GetChild(i);
            m_poolDic.Add(thisChild.name, thisChild.GetComponent<ObjPool>());
        }
    }
    ///其他管理对象池的代码
}

对象池代码

public class ObjPool : MonoBehaviour
{
    [SerializeField]
    GameObject obj;
    [SerializeField]
    GameObject QiangKou;
    [SerializeField]
    int Maxnum = 0;


    void Start()
    {
        Initiate();
    }
    protected Queue m_queue;
    void Initiate()
    {
        m_queue = new Queue(Maxnum);
        for (int i = 0; i < Maxnum; i++)
        {
            var newobj = Instantiate(obj);
            newobj.transform.position = Vector3.zero;
            newobj.name = "bullet" + i.ToString();
            newobj.transform.SetParent(this.transform);
            m_queue.Enqueue(newobj);
            newobj.SetActive(false);
        }
    }

    void Shot(GameObject bullet)
    {
        Debug.Log("发射子弹:" + bullet.name);
        StartCoroutine(Fire(bullet));
    }

    IEnumerator Fire(GameObject bullet)
    {
        Debug.Log("发射:" + bullet.name);
        while (bullet.transform.position.x < 1500)
        {
            bullet.transform.position = new Vector3(bullet.transform.position.x + 10f, bullet.transform.position.y, bullet.transform.position.z);
            yield return new WaitForSeconds(0.02f);
        }
        InPool(bullet);
    }
    void OutPool()
    {
        GameObject bullet = (GameObject)m_queue.Dequeue();
        bullet.transform.position = QiangKou.transform.position;
        bullet.SetActive(true);
        Shot(bullet);
    }
    void InPool(GameObject bullet)
    {
        bullet.SetActive(false);
        m_queue.Enqueue(bullet);
    }
    public void OnShotClick()
    {
        Debug.Log("准备发射");
        OutPool();
    }
}

调用对象池管理器单例的其他类

public class GunsManager : MonoBehaviour
{
    void Start()
    {
        ObjPoolManager.Instance.initPool(this.gameObject);
    }
}

在这里插入图片描述
单对象池效果展示,其中白色块代表枪口,黑色块代表子弹
在这里插入图片描述
多对象池效果展示,三色按钮代表了三种不同的枪,点击按钮发射子弹

想要完善系统,则需要拓展对象池管理器类,然后通过其他类进行调用对象池管理器类。

在上述例子中,我只是将对象池类挂载到了每个按钮上,然后根据按钮触发事件来发射子弹。那么同样发射的代码就需要重复三次。更好的方式应当是统一使用一个按钮来执行发射对象池事件,可以直接定义在对象池管理器上,然后根据索引值来选择操作的对象池。

例如切枪操作,也可以通过修改对象池管理器的字典,通过对象池管理器,可以更灵活地管理对象池。

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

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

相关文章

vue中人员导出功能实现

大纲&#xff1a; 1、导出定义的export.js文件 代码展示 import axios from axios //导出一 export const exportExcel (url, params, name, type post) > {// url url路径 params 查询参数 name 文件名 type 请求方式axios[type](url, params, {responseType: blob,}).t…

微信小程序多码融合

1、多码融合实现 如果需要实现扫码关注、跳转页面、扫码充电以及第三方融合扫码充电的需求&#xff0c;通过“扫普通链接二维码打开小程序” 的功能采用hlht协议的方式进行融合&#xff0c;使用代码生成新的二维码&#xff0c;二维码内容格式如下&#xff1a; hlht://9900000…

性能测试基础知识(三)性能指标

性能测试基础知识&#xff08;三&#xff09;性能指标 前言一、时间特性1、响应时间2、并发数3、吞吐量&#xff08;TPS&#xff09; 二、资源特性1、CPU利用率2、内存利用率3、I/O利用率4、网络带宽使用率5、网络传输速率&#xff08;MB/s&#xff09; 三、实例场景 前言 性能…

ES6系列之let、const、箭头函数使用的坑

变量提升块级作用域的重要性箭头函数this的指向rest参数和arguments 1.ECMAScript与Js的关系 2.Babel转码器 Babel是一个广泛使用的ES6转码器&#xff0c;可以将ES6代码转为ES5代码&#xff0c;从而在老版本的浏览器执行。这意味着&#xff0c;你可以用ES6的方式编写程序&…

MyBatis的使用方法

文章目录 一、MyBatis的创建准备工作 二、MyBatis的使用1.项目分层2.业务代码1&#xff09;使用XML的方法2&#xff09;直接使用注解 总结 一、MyBatis的创建 准备工作 1.添加依赖 旧项目 方法一&#xff1a;在pom.xml中添加MyBatis和MySQL Diver依赖 <!-- 添加 MyBati…

QC API全系列揭秘之Test Execution操作(全网首发)

目录 一、QC简介&#xff1a; 二、写作目的&#xff1a; 三、解决问题&#xff1a; 四、本文重点&#xff1a; 五、QC接口规范&#xff1a; 六、QC接口操作Test Execution&#xff1a; 定义全局变量 QC服务器连接、登录&#xff08;身份验证&#xff09;及项目连接 重点…

小程序商城系统的开发方式及优缺点分析

小程序商城系统是一种新型的电子商务平台&#xff0c;它通过小程序的形式为商家提供了一种全新的销售渠道&#xff0c;同时也为消费者提供了一种便捷的购物体验。小程序商城系统具有低成本、快速上线、易于维护等特点&#xff0c;因此在市场上受到了广泛的关注和应用。这里就小…

【设计模式——学习笔记】23种设计模式——模板方法模式Template Method(原理讲解+应用场景介绍+案例介绍+Java代码实现)

介绍 基本介绍 模板方法模式&#xff0c;又叫模板模式&#xff0c;在一个抽象类中定义了一个执行它的其他方法的公开模板方法&#xff0c;子类可以按需重写抽象类的抽象方法简单说&#xff0c;模板方法模式 定义一个操作中的算法&#xff08;或者说流程&#xff09;的骨架&am…

Vue引入

1. vue引入 第一种方法&#xff1a;在线引入 <script src"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 第二种方法&#xff1a;本地引入 2. 语法学习 el用于绑定id&#xff0c;data用于定义数据如下例题 <!DOCTYPE html> <html…

xinput1_4.dll丢失的解决方法,三种解决方法分享

xinput1_4.dll是一个动态链接库文件&#xff08;DLL&#xff09;&#xff0c;它是Microsoft DirectX的一部分&#xff0c;用于处理游戏控制器输入。当你的电脑提示xinput1_4.dll文件丢失时&#xff0c;意味着与这个文件相关的游戏或应用程序无法正常运行。 当你的电脑提示xinp…

正负样本分配策略(OTA, SimOTA,TAS)

文章目录 OTASimOTATALATSS OTA 论文&#xff1a;《OTA: Optimal Transport Assignment for Object Detection》 代码&#xff1a;Megvii-BaseDetection/OTA 标签分配算法 目标CNN-based的目标检测器是预测 pre-defined anchors 的类别 (cls) 以及偏移量 (reg) 。 为了训练目标…

go 结构体 - 值类型、引用类型 - 结构体转json类型 - 指针类型的种类 - 结构体方法 - 继承 - 多态(interface接口) - 练习

目录 一、结构体 1、python 与 go面向对象的实现&#xff1a; 2、初用GO中的结构体&#xff1a;&#xff08;实例化一个值类型的数据&#xff08;结构体&#xff09;&#xff09; 输出结果不同的三种方式 3、实例化一个引用类型的数据&#xff08;结构体&#xff09; 4、…

esp32 wifi无线透传

wifi无线透传 目录 wifi无线透传[TOC](目录) 一、Esp32代码1.1 下载烧写 二、星空内网穿透配置2.1 平台注册2.2 充值2元(用于实名认证)2.3 实名认证2.4 创建隧道2.5 下载软件2.6 配置文件2.7 启动服务 因为常见的无线传输视频或图片&#xff0c;只能在局域网内中实现&#xff0…

【并发专题】手写MyReentantLock

分析 ReentantLock的特点如下&#xff1a; 首先是继承自AQS的可中断可以设置超时时间可以切换公平锁/非公平锁支持多个条件变量支持可重入 事实上&#xff0c;上面的很多东西AQS已经帮忙实现了&#xff0c;所以想要复刻一个不是很难。仔细观察一下源码&#xff0c;我们需要重…

Text-to-SQL小白入门(一)

摘要 本文主要介绍了Text-to-SQL研究的定义、意义、研究方法以及未来展望&#xff0c;主要是对Text-to-SQL领域进行一个初步的认识和了解&#xff0c;适合初学者入门了解。 1 引言 作为Text-to-SQL领域的小白&#xff0c;学习该领域的最好方式就是看最新的综述文章&#xff…

Junit4入门之什么是单元测试?

干了一年多的后端了&#xff0c;从来没有了解过单元测试。虽然我知道测试不仅仅是测试们的任务&#xff0c;后端也要进行自测来保证自己的代码的可用性&#xff0c;但我一直都只是用postman来实施的&#xff0c;调用调通了即可。虽然我也知道Junit是用于测试的软件&#xff0c;…

几种常用到的 Hybrid App 框架方案

移动操作系统在经历了诸神混战之后&#xff0c;BlackBerry OS、Symbian OS、Windows Phone等早期的移动操作系统逐渐因失去竞争力而退出。目前&#xff0c;市场上主要只剩下安卓和iOS两大阵营&#xff0c;使得iOS和安卓工程师成为抢手资源。然而&#xff0c;由于两者系统的差异…

学了python的心得体会200字,学python心得体会1000字

大家好&#xff0c;本文将围绕学了python的心得体会200字展开说明&#xff0c;学python心得体会1000字是一个很多人都想弄明白的事情&#xff0c;想搞清楚学python心得体会800字需要先了解以下几个事情。 一、个人学期总结 本学期在missdu的带领下&#xff0c;进行了python的学…

Jenkins通过OpenSSH发布WinServer2016

上一篇文章> Jenkins集成SonarQube代码质量检测 一、实验环境 jenkins环境 jenkins入门与安装 容器为docker 主机IP系统版本jenkins10.10.10.10rhel7.5 二、OpenSSH安装 1、下载 官网地址&#xff1a;https://learn.microsoft.com/zh-cn/windows-server/administration/op…