UnityVR--Managers--对象池1

news2024/11/18 3:39:18

  本篇中使用的API:gameObject.CompareTag("标签")、UnityEvent()事件管理、ObjectPool<GameObject>()对象池

  参照unity官方教程:Hi ObjectPool 


目录

1. 应用场景

   2. 对象池的原理

  3. 查看资源消耗情况

  4. 不使用对象池实现的情形

  5. 使用对象池

  6. 是否使用对象池的比较


1. 应用场景

  对象池管理的作用场景是当短时间出现大量对象时,合理管理对象的出现、消亡,从而优化内存、GPU性能管理,不至于出现卡顿等现象,这在基于移动平台的场景中尤为明显。比如说下面这个游戏结束时,大量奖品突然涌现的画面:

  另外,还有在游戏中经常看到的,使用到大量子弹、受到大量怪兽攻击等的画面。

   2. 对象池的原理

  在上面的场景中,如果在短时间内大量地创建和销毁重复的对象,就会消耗大量资源,并产生大量的垃圾。在垃圾回收的时候,会中断执行程序代码,直到回收完成。这个时间可能持续1ms~几百ms,在PC上可能直观感受不明显,但到移动端、VR一体机或者其他低端终端运行时就会看到游戏的卡顿。

  因此,现在的Unity使用了ObjectPool的思想,即把场景中需要大量出现的GameObject存入一个数据集(字典或数组),就像一个池子一样。需要在场景中呈现时就激活它——SetActive(true),而不是重新去创建,不用了就让它失活——SetActive(false),而不是销毁它。  

  3. 查看资源消耗情况

  以下比较使用和不使用对象池的情况,对于运行时资源占用的情况,可以在菜单Windows->Analysis->Profiler查看,可以选择Scripts(脚本)和GarbageCollector(垃圾回收),重点查看这两项:

  

  4. 不使用对象池实现的情形

  如果不使用对象池管理,那么在Update()中会不断地使用Instantiate实例化物体,并且用Destroy()销毁物体,会让系统不断地分配内存和回收内存。下面写一个不使用对象池管理的例子,包含Trophy.cs和TrophyManager.cs两段代码。

  (1)建立简单测试场景:在场景中建立一个空节点(Trophy),把需要大量显示的对象都放在它底下,这些对象每一个都要假设Rigidbody和Collider,等下需要它们触地消失。

  

   (2)Trophy代码:动态地挂在每个奖品上,其中:

    定义一个事件destroyEvent,用于触发TrophyManager中的销毁(Destroy)回调;

    使用一个OnCollisionEnter控制奖品接触其他碰撞器后, 激活destroyEvent事件。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;//使用事件时,需要加载这个命名空间
//对象:控制每个奖品,动态地挂载在每一个礼物上
//作用:让礼物碰到其他碰撞器就消失

public class Trophy : MonoBehaviour
{
    public UnityEvent destroyEvent=new UnityEvent();
    //公开定义一个用于销毁的事件,在TrophyManager中监听它并调用Destroy
    public  bool isDestroy = false;
    //判断是否已经销毁,以免重复触碰不同的Collider多次销毁

    public void OnEnable()
    {//Trophy有效时(不管是激活的还是新创建的),先把isDestroy标记置为false
        isDestroy = false;
    }

    private void OnCollisionEnter(Collision collision)
    {//碰撞事件
        if(collision.gameObject.CompareTag("Ground") &&!isDestroy)
        {//如果碰到了标签为"Ground"的碰撞器,且Trophy本身也是有效状态
            isDestroy = true;
            destroyEvent?.Invoke(); //触发销毁事件
        }
    }
}

   (3)TrophyManager代码:这里只要做两个步骤:第一步,创建新的Trophy;第二步:监听到销毁事件后,销毁Trophy。

public class TrophyManager : MonoBehaviour
{
    public GameObject[] trophies; //建立一个数组,用于存放所有的trophy对象
    public int number = 50; //定义在场上新建的trophy对象的数量

    void Update()
    {        
        for(int i=0;i<number;i++)
        {
            var trophy=Instantiate(trophies[Random.Range(0, trophies.Length)], transform);
            //实例化对象到父物体(挂载TrophyManager脚本的对象)下
            
            trophy.transform.localPosition= Random.insideUnitSphere;
            //Random.insideUnitSphere返回半径为1的球体内的一个随机点

            trophy.AddComponent<Trophy>().destroyEvent.AddListener(() =>
            {   //监听Trophy脚本中的销毁事件
                Destroy(Trophy);
            });

  这个脚本挂在所有奖品的父节点上,并将所有奖品对象拖入数组当中。这样就能执行礼物喷涌的效果。

  5. 使用对象池

  使用Unity定义的对象池,需要在开始使用一下命名空间:using UnityEngine.Pool;

  Trophy.cs代码不做更改,TrophyManager.cs代码修改如下

public class TrophyManager : MonoBehaviour
{
    public GameObject[] trophies;
    public int number = 50;
    private ObjectPool<GameObject> trophyPool; //定义一个对象池
    void Start()
    {
        trophyPool = new ObjectPool<GameObject>(createFunc: () =>
        {   //建立Trophy的对象池trophyPool,类型为<GameObject>类型
            //设置对象池的回调
            var trophy = Instantiate(trophies[Random.Range(0, trophies.Length)], transform);
            trophy.AddComponent<Trophy>().destroyEvent.AddListener(call: () =>
            {   //监听Trophy脚本的destroyEvent事件
                trophyPool.Release(trophy);  //不销毁,而是在对象池中回收
            });
            return trophy;
        },
            actionOnGet: (go) =>
            {  //调用时激活
                go.SetActive(true);
                go.transform.localPosition = Random.insideUnitSphere;
            },
            actionOnRelease: (go) =>
            {  //失活
                go.SetActive(false);
            },
            actionOnDestroy: (go) =>
            {  //销毁
                Destroy(go);
            });
    }

  这个λ表达式比较长,主要是因为对象池的声明就很长,包括了建立对象池、调用、释放、销毁、检查内容、初始容量、最大容量这些参数:

public ObjectPool(Func<T> createFunc, Action<T> actionOnGet = null, Action<T> actionOnRelease = null, Action<T> actionOnDestroy = null, bool collectionCheck = true, int defaultCapacity = 10, int maxSize = 10000)

  6. 是否使用对象池的比较

  如果在PC端运行,上面两段代码的区别并不能直观感受出来,可以打开Profiler查看一下资源占用的情况,下面左图是不使用对象池,右图使用了对象池,可以看到GC垃圾回收数量右边明显少于左边。

  

 

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

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

相关文章

mysql 备库延迟问题

备库延迟原因&#xff1a; log传送开销小&#xff0c;消费relay log 超时 备库性能不如主库 备库承担更多SQL分析 主库是多线程执行&#xff0c;备库是单线程执行解析relay log 处理方法&#xff1a; 主备使用相同的机器 备库关闭log实时落盘 增加从库数量&#xff0c;…

Sentinel-2数据下载及处理

数据下载网站&#xff1a;欧空局官网&#xff08;需注册并登录&#xff09; https://scihub.copernicus.eu/dhus/#/home 哨兵2 L1C数据波段信息 哨兵2 L1C数据时间&#xff1a;2015-06-23至now 由于数据量大&#xff0c;考虑服务器压力&#xff0c;哨兵2号数据直接下载的时间跨…

[web安全原理分析]-XXE漏洞入门

前言 XXE漏洞 XXE漏洞全称(XML External Entity Injection)即xml外部实体注入漏洞&#xff0c;XXE漏洞发生在应用程序解析XML输入时&#xff0c;没有禁止外部实体的加载&#xff0c;导致可加载恶意外部文件&#xff0c;造成文件读取、命令执行、内网端口扫描、攻击内网网站、…

头歌计算机组成原理实验—运算器设计(10) 第10关:补码一位乘法器设计

第10关&#xff1a;补码一位乘法器设计 实验目的 学生掌握补码一位乘法运算的基本原理&#xff0c;熟练掌握 Logisim 寄存器电路的使用&#xff0c;能在 Logisim 平台中设计实现一个8*8 位的补码 Booth一位乘法器。 视频讲解 ####实验内容 在 alu.circ 文件中的补码一位乘法…

Linux基础——权限

1. Linux下的用户 在 Linux 操作系统中&#xff0c;有两种主要类型的用户账户&#xff0c;分别是普通用户账户和超级用户账户&#xff08;也称为 root 用户账户&#xff09;。 普通用户账户&#xff08;user&#xff09;是在 Linux 系统上创建的普通账户&#xff0c;可以用于日…

图片隐写(一)

文件隐藏 binwalk binwalk -e filename foremost foremost filename steghide & stegseek Install sudo apt-get install steghidestegseek Use steghide extract -sf filename -p passwordtime stegseek secret.file aaa.txt dd 文本隐藏 二进制文件末尾 or 文…

ffmpeg rtsp解析

一、 rtsp 协议说明 rtsp的协议层级 rtsp 属于应用层&#xff0c; 使用tcp传输&#xff0c;主要是传递服务器的一些信息&#xff0c;实现流连接。播放 暂停 销毁等控制 rtp 实现音视频数据包的发送&#xff0c;通过RTSP等协议的SDP信息协商好了RTP数据包的发送目的和传输方式…

UNIX网络编程卷一 学习笔记 第十四章 高级I/O函数

有3种方式可在涉及套接字的IO操作上设置超时方法&#xff1a; 1.调用alarm&#xff0c;它在指定超时期满时产生SIGALRM信号。此方法涉及信号处理&#xff0c;而信号处理在不同的实现上存在差异&#xff0c;且此方法可能干扰进程中已经执行过的alarm调用&#xff0c;可能使之前已…

【LLM系列之Tokenizer】如何科学地训练一个LLM分词器

1 背景与基础 1.1 为什么需要分词 对于人而言&#xff0c;在我们学会阅读之前&#xff0c;仍然可以理解语言。比如当你开始上学时&#xff0c;即使你不知道名词和动词之间的区别&#xff0c;但是你已经可以和你的同学交谈了&#xff0c;比如“我喜欢吃香蕉”&#xff0c;孩子…

vmware ubuntu突然无法联网的一种解决方案

记录一下vmware突然无法联网的一种解决方案。此法未必适用所有无法联网情形。Good Luck then. 今天使用vmware的ubuntu 18.04时&#xff0c;突然无法联网。Firefox在访问百度时显示“The proxy server is refusing connections”&#xff0c;随即检查了浏览器的proxy设置&#…

详解Jetpack Compose中的状态管理与使用

前言 引用一段官方描述&#xff0c;如下 由于 Compose 是声明式工具集&#xff0c;因此更新它的唯一方法是通过新参数调用同一可组合项。这些参数是界面状态的表现形式。每当状态更新时&#xff0c;都会发生重组。因此&#xff0c;TextField 不会像在基于 XML 的命令式视图中那…

头歌计算机组成原理实验—运算器设计(9)第9关:原码一位乘法器设计

第9关&#xff1a;原码一位乘法器设计 实验目的 学生掌握原码一位乘法运算的基本原理&#xff0c;熟练掌握 Logisim 寄存器电路的使用&#xff0c;能在 Logisim 平台中设计实现一个 8*8位的无符号数乘法器。 视频讲解 ####实验内容 在 alu.circ 文件中的原码一位乘法器子电…

分布式消息中间件RocketMQ概述

RocketMQ 概述 MQ概述 MQ简介 ​ MQ&#xff0c;Message Queue&#xff0c;是一种提供消息队列服务的中间件&#xff0c;也称为消息中间件&#xff0c;是一套提供了消息生产、存储、消费全过程API的软件系统。消息即数据。一般消息的体量不会很大。 MQ用途 在网络上上可以查…

React学习笔记五-props

此文章是本人在学习React的时候&#xff0c;写下的学习笔记&#xff0c;在此纪录和分享。此为第五篇&#xff0c;主要介绍react中的props。 目录 1.props的基本使用 2.props的批量传递 2.1展开运算符的复习 2.1.1数组中的展开运算符 2.1.2函数中的展开运算符 2.1.3构造字面…

部署图的画法

部署图画法 1.部署图 1.1含义 部署图是把软件制品装配到计算机节点以及配置软件环境的工作 软件部署包含环境部署和软件制品部署 1.2软件部署 软件部署通过部署图对软件进行建模 1.3部署图 部署图常见的有制品 节点 设备 运行环境和部署规范 1.4部署图关系 在UML&…

UnityVR--Managers--对象池2

目录 前言 基本结构 对象池代码 对象池管理器代码 使用 总结 前言 经过上一篇对象池1的了解&#xff0c;已经做到了使用Unity自带的ObjectPool进行内存优化。本篇自己构建一个对象池管理器&#xff08;Manager&#xff09;&#xff0c;实现对象池的创建、删除、加载资源…

机器视觉工程师很苦吗?年轻人不怕苦,就怕学不到东西,机器视觉销售>项目经理>视觉>电气>机械>老板

年轻人不怕苦&#xff0c;就怕学不到东西。 对于年轻人来说&#xff0c;需要规划&#xff0c;更需要发展。如果学不到东西&#xff0c;就会限制其发展&#xff0c;最重要的体现就是限制待遇上限。 一个非标自动化公司出差的频次&#xff08;各个公司略有差别&#xff0c;大多…

062:cesium设置泛光折线材质(material-6)

第062个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置泛光折线材质,请参考源代码,了解PolylineGlowMaterialProperty的应用。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共89行)相关API参考:专…

蓝桥:前端开发笔面必刷题——Day2 数组(三)

文章目录 &#x1f4cb;前言&#x1f3af;两数之和 II&#x1f4da;题目内容✅解答 &#x1f3af;移除元素&#x1f4da;题目内容✅解答 &#x1f3af;有序数组的平方&#x1f4da;题目内容✅解答 &#x1f3af;三数之和&#x1f4da;题目内容✅解答 &#x1f4dd;最后 &#x…

Cloud Studio 内核升级之持续优化

前言 Cloud Studio 是基于浏览器的集成式开发环境&#xff08;IDE&#xff09;&#xff0c;为开发者提供了一个永不间断的云端工作站。用户在使用 Cloud Studio 时无需安装&#xff0c;随时随地打开浏览器就能使用。云端开发体验与本地几乎一样&#xff0c;上手门槛更低&#…