【Unity Optimize】使用对象池(Object Pooling)优化项目

news2024/11/26 5:45:50

目录

  • 1 对象池(Object Pooling)介绍
  • 2 实现对象池脚本
  • 3 使用对象池生成Cube
  • 4 效果展示
  • 5 Unity资源商店的对象池插件

1 对象池(Object Pooling)介绍

Unity中的对象池(Object Pooling)是一种用于提高游戏性能和减少资源浪费的优化方案。尤其是在需要快速创建和销毁游戏对象时,比如在一些射击游戏中发射子弹时。

对象池在游戏运行前预先创建一定数量的对象,将它们存储在一个重用字典(或者数组)中,在需要时只是激活或停用所需的游戏对象,实际上只是循环使用对象,而不是使用原生的Instantiate和Destroy方法创建和销毁对象。我们将这个存储所有激活或非激活对象的字典(或者数组)称为池子。这样可以大大减少频繁创建和销毁对象的开销,并且可以减少内存分配和垃圾回收,提高游戏的运行效率。

2 实现对象池脚本

实现一个对象池脚本ObjectPool.cs,利用序列化的游戏对象预制体来快速生成和回收大量相同类型的物体。

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 此类实现了对象池模式,用于创建和管理大量的游戏对象
/// </summary>
public class ObjectPool
{
    private readonly GameObject prefab; // 预制体对象,用来创建新的游戏对象
    private readonly int initialPoolSize; // 初始对象池大小,即预先生成的游戏对象数量
    private readonly bool canGrow; // 是否可以动态扩展对象池大小
    private readonly List<GameObject> pool = new List<GameObject>(); // 游戏对象池

    // 单例对象,用于全局访问对象池
    public static ObjectPool Instance;

    // 私有无参构造函数,避免从外部创建对象池实例
    private ObjectPool()
    {
        this.prefab = null;
        this.initialPoolSize = 0;
        this.canGrow = false;
    }

    // 构造函数,创建对象池实例,并初始化预制体对象、对象池大小、是否可扩展等参数
    public ObjectPool(GameObject prefab, int initialPoolSize, bool canGrow)
    {
        this.prefab = prefab;
        this.initialPoolSize = initialPoolSize;
        this.canGrow = canGrow;

        InitializePool(); // 初始化对象池
    }

    // 从对象池中获取未被使用的游戏对象,如果对象池已满并且可以扩展,将会创建新的游戏对象
    public GameObject GetObject()
    {
        GameObject obj = pool.Find(o => !o.activeSelf); // 查找未被激活的游戏对象
        if (obj == null && canGrow) // 如果对象池满了并且可以扩展
        {
            obj = AddObjectToPool(); // 创建新的游戏对象
        }

        if (obj != null)
        {
            obj.SetActive(true); // 激活游戏对象
        }

        return obj;
    }

    // 将游戏对象返回到对象池中
    public void ReturnObject(GameObject obj)
    {
        obj.SetActive(false); // 取消游戏对象的激活状态
    }

    // 初始化对象池,创建一定数量的游戏对象并加入到游戏对象池中
    private void InitializePool()
    {
        for (int i = 0; i < initialPoolSize; i++)
        {
            GameObject obj = GameObject.Instantiate(prefab); // 创建新的游戏对象
            obj.SetActive(false); // 初始时将游戏对象设置为未激活状态
            pool.Add(obj); // 将游戏对象加入到对象池中
        }
    }

    // 向对象池中添加新的游戏对象
    private GameObject AddObjectToPool()
    {
        GameObject obj = GameObject.Instantiate(prefab); // 创建新的游戏对象
        obj.SetActive(false); // 初始时将游戏对象设置为未激活状态
        pool.Add(obj); // 将游戏对象加入到对象池中
        return obj;
    }
}

3 使用对象池生成Cube

使用上述的对象池,编写脚本NewCube.cs,在鼠标左键点击位置生成Cube,点击鼠标右键可以销毁一个个之前生成的Cube,按键盘的X键可以销毁所有的cube。

using System.Collections.Generic;
using UnityEngine;

public class NewCube : MonoBehaviour
{
    [SerializeField]
    private GameObject cubePrefab; // Cube预制体
    [SerializeField]
    private float spawnHeight = 10f; // 在鼠标落下的位置生成Cube时使用的高度值
    [SerializeField] 
    private int initialPoolSize = 20; // 初始化对象池时的初始大小
    [SerializeField] 
    private bool canGrow = true; // 当对象池中的对象数量达到最大值时,canGrow 为 true 的话会增加对象池的大小

    private GameObject currentCube; // 当前的Cube游戏对象
    private Vector3 lastCubePosition; // 上一个Cube的位置
    private List<GameObject> cubesList = new List<GameObject>(); // 用于记录所有的Cube
    

    private void Start()
    {
        ObjectPool.Instance = new ObjectPool(cubePrefab, initialPoolSize, canGrow);
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0)) // 鼠标左键按下
        {
            SpawnCube();
        }
        else if (Input.GetMouseButtonDown(1)) // 鼠标右键按下
        {
            DeleteLastCube();
        }

        if (Input.GetKeyDown(KeyCode.X)) // X键按下
        {
            ClearCubes();
        }
    }

    //生成Cube对象
    private void SpawnCube()
    {
        Vector3 mousePos = Input.mousePosition;
        mousePos.z = spawnHeight;
        Vector3 spawnPos = Camera.main.ScreenToWorldPoint(mousePos);

        if (currentCube != null)
        {
            lastCubePosition = currentCube.transform.position;
        }

        currentCube = ObjectPool.Instance.GetObject();
        currentCube.transform.position = spawnPos;
        currentCube.SetActive(true);

        Rigidbody rb = currentCube.GetComponent<Rigidbody>();
        rb.velocity = Vector3.zero;
        rb.useGravity = true;

        lastCubePosition = currentCube.transform.position;

        cubesList.Add(currentCube); // 将当前Cube添加到列表中
    }

    // 删除上一个生成的 Cube 对象
    private void DeleteLastCube()
    {
        if (cubesList.Count > 0)
        {
            ObjectPool.Instance.ReturnObject(cubesList[cubesList.Count - 1]);
            cubesList.RemoveAt(cubesList.Count - 1); // 从列表中删除最后一个Cube
        }

        if (cubesList.Count > 0)
        {
            lastCubePosition = cubesList[cubesList.Count - 1].transform.position;
        }
        else
        {
            lastCubePosition = Vector3.zero;
        }
    }

    // 销毁所有的Cube对象
    private void ClearCubes()
    {
        int childCount = cubesList.Count;

        for (int i = childCount - 1; i >= 0; i--)
        {
            GameObject obj = cubesList[i].gameObject;
            ObjectPool.Instance.ReturnObject(obj);
        }

        currentCube = null;
    }
}

4 效果展示

首先,在Unity中在初始位置(0,0,0)处新建一个平面,适当调大其Scale的X值和Z值;接着,新建一个Cube物体,为其添加上Rigidbody组件(重力效果)并将其做成预制体;在场景中新建一个空物体,挂载NewCube脚本后,将做好的Cube预制体拖拽至其CubePrefab参数处。

在这里插入图片描述

视频演示:

5 Unity资源商店的对象池插件

Unity资源商店中已封装好的对象池插件:Lean Pool
也可以直接使用Unity内置的对象池API,使用以下命名空间引入:

using UnityEngine.Pool;

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

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

相关文章

教程详解|如何在PICO眼镜中接入VR全景?

伴随着《万人狂欢泼水节》首场VR直播开启&#xff0c;PICO视频正式推出《跟着PICO去旅行》系列VR文旅直播节目&#xff0c;通过PICO眼镜结合VR技术向众多观众展现更多祖国的大好山河&#xff0c;通过PICO眼镜身临其境地感受云游世界的美妙。 经历了十余年的发展&#xff0c;VR…

esp32环境安装教程---arduino IDE

前言 &#xff08;1&#xff09;最近突然对esp32感兴趣了&#xff0c;源于逛油管的时候&#xff0c;总是看到很多大佬使用esp32cam进行网络图传&#xff0c;做摄像头。个人比较感兴趣&#xff0c;在环境安装过程遇到了很多坑&#xff0c;所以在此跟大家分享一下。 &#xff08;…

了解ChatGPT的最便捷有效方式是跟对人

毕业后&#xff0c;工作原因&#xff0c;我自己的CSND就没有更新过。现在更新一篇有关chatGPT的快速入门指南。 一、什么是chatGPT 2022年12月底&#xff0c;你问我这个问题&#xff0c;我还真得好好跟你解释一下。目前这个阶段的火热程度&#xff0c;这里不再啰嗦了。基于GPT…

AI时代的赚钱思路:23岁女网红如何利用AI技术年入4亿?

一、AI技术为网红赚钱创造新途径 23岁美国网红Caryn Marjorie&#xff08;卡琳玛乔丽&#xff09;正同时交往1000多个男朋友。 作为一个在Snapchat上坐拥180万粉丝的美女&#xff0c;她利用人工智能&#xff08;AI&#xff09;技术&#xff0c;打造了一个AI版本的自己&#x…

鲲鹏昇腾开发者峰会开幕 星辰天合发布新一代天合翔宇一体机

近日&#xff0c;主题为“创未来 享非凡”的鲲鹏昇腾开发者峰会 2023 在东莞松山湖开幕&#xff0c;此次大会旨在帮助开发者深入了解鲲鹏、昇腾全栈技术&#xff0c;加速行业数智化的技术、产品和解决方案创新。 作为鲲鹏生态重要合作伙伴&#xff0c;XSKY星辰天合获邀参加此次…

【利用AI让知识体系化】入门Koa框架

思维导图 文章目录 思维导图一、介绍Koa什么是KoaKoa的历史Koa的特点 二、基本使用安装KoaHello World中间件路由错误处理 三、进阶使用静态资源管理Session管理文件上传表单处理HTTPS支持 四、Koa中间件中间件的概念Koa的洋葱模型常用中间件的介绍自定义中间件的编写 五、异步…

这个水平来面试也敢要20K?还是3年经验的测试工程师....

起因 老板觉得现在公司部门里都是男的&#xff0c;缺少一点阴柔之气&#xff0c;想平衡一下&#xff0c;正巧当时互联网公司倒了一大批&#xff0c;大批简历投到公司&#xff0c;老板以为自己也是技术出身&#xff0c;就想着要招了一个三年工作经验的女测试员&#xff0c;要价…

ANR实战案例1 - Google广告导致ANR解决

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、AdLoader$Builder广告构建ANR二、AdView布局动态创建案例总结 前言 一、AdLoa…

Linux 安装JDK详解

安装步骤 1. 从官网下载安装包保存到 /home/download 下面 2.下载后解压到指定目录&#xff0c;例如&#xff1a;/usr/java/jdk11 3. 解压压缩包 [rootlocalhost linux-x86-64]# cd /usr/java/jdk11 [rootlocalhost jdk11]# [rootlocalhost jdk11]# [rootlocalhost jdk11]…

【换模型更简单】如何用 Serverless 一键部署 Stable Diffusion?

作者&#xff1a;寒斜 本篇章是阿里云函数计算部署 Stable Diffusion 系列的第三篇&#xff0c;如果说第一篇是尝试使用云服务来解决用户本地部署 Stable Diffusion 的问题&#xff08;显卡成本&#xff0c;部署技术复杂&#xff09;&#xff0c;第二篇是面向技术同学解决云服…

Wijmo 2023 v1 Crack

改进了 FlexGrid&#xff0c;支持 React 18 严格模式和可访问性。 5月 15&#xff0c; 2023 - 10&#xff1a;51 新版本 特征 改进了对 React 18 的支持 - 添加了对 React 18 严格模式的支持&#xff0c;可帮助开发人员在开发过程中查找常见错误。辅助功能改进 - 以下是此版本中…

postman工具使用中,遇到的简单疑问

post请求和get请求有什么区别&#xff1f; GET请求和POST请求都是HTTP协议中的两种常见请求方式&#xff0c;主要区别如下&#xff1a; GET请求在请求URL的后面附带参数&#xff0c;而POST请求则将参数包含在请求体中。 GET请求的数据是以查询字符串的形式传递的&#xff0c;而…

JavaEE(系列4) -- 多线程(线程的状态)

目录 观察线程的所有状态 1. new状态 2. TERMINATED 状态 3. RUNNABLE 就绪状态,运行状态 4. TIMED_WAITNG 休眠状态 5. BLOCKED 表示等待锁出现的状态 6. WAITING 使用wait方法出现的状态 观察线程的所有状态 线程的状态是一个枚举类型 public class test2 {public static v…

【AI绘图 丨 Midjourney 系列教程二】— 初识超火的AI绘画神器Midjourney

今天起&#xff0c;由 Midjourney 打头阵&#xff0c;让我们开始共同探索一系列的 AI 领域革命性作品&#xff0c;包括 Midjourney、Stable Diffusion、ChatGPT 等等&#xff0c;学习这些新时代的魔法和它的咒语。 写在前面 官方文档是最好的入门课程。相较于市面上琳琅满目的…

git入门(必看)

git入门 git简介 文章目录 git入门git简介版本控制系统发展可以分为三个阶段&#xff1a; Git 的诞生Git 诞生的背景Linus 两周完成 GitGit 的发展壮大 git安装在 Windows 上安装在 macOS 上安装在 Linux 上安装 Git环境配置配置用户名和邮件地址检查配置Git 颜色配置Git忽略文…

STC15单片机+nRF24L01通讯利用PCA输入捕获触发

STC15单片机+nRF24L01通讯利用PCA输入捕获触发 📍相关篇《STC单片机 NRF24L01通讯实验》📌《STC单片机 NRF24L01通讯带状态反馈》🔖验证对象:STC15L2K60S2📍STC15L2K60S2自制系统板硬件开源地址:https://oshwhub.com/perseverance51/stc15l2k60s2-ji-tong-ban⏱时钟频…

C++ - AVL树

之前的文章中我们学习过二叉搜索树&#xff0c;学习完该部分之后&#xff0c;在进行OJ的练习和思考中会发现如果一颗搜索树由于初始结点选择的不好这棵树就会变成成一颗歪脖子树&#xff0c;这样搜索的效率反而会变的不是很理想。那么在今天的文章中我们就要来介绍一种基于搜索…

【啃书C++Primer5】-c++有些理论基础需要了解,墙裂建议看看原书,有太多细节需要注意了

任何常用的编程语言都具备一组公共的语法特征&#xff0c;不同语言仅在特征的细节上有所区别。要想学习并掌握–种编程语言&#xff0c;理解其语法特征的实现细节是第一步。最基本的特征包括: 整型、字符型等内置类型变量&#xff0c;用来为对象命名 表达式和语句&#xff0c;…

10款最好的Photoshop替代软件

Photoshop作为一种老式的设计软件&#xff0c;在世界上享有很高的声誉&#xff0c;所以很多人说学习设计&#xff0c;第一反应是学习Photoshop&#xff0c;结果打开电脑发现&#xff1a;配置不够&#xff0c;预算不足&#xff0c;课程有点困难&#xff0c;因为这条路不起作用&a…

嵌入式系统入门基础知识分析(一)

目录 ​编辑 一、什么是嵌入式 二、嵌入式系统的组成 三、实时系统