【unity进阶知识4】封装unity协程工具,避免 GC(垃圾回收)

news2024/9/29 9:37:49

文章目录

  • 前言
  • 封装协程工具类,避免 GC(垃圾回收)
  • 使用
    • 1.使用默认方式使用协程
    • 2.使用自定义的 CoroutineTool 工具类来等待不同的时间
  • 完结

前言

在 Unity 中,使用 yield return null 、yield return new WaitForEndOfFrame()等会导致 GC(垃圾回收)开销。

  • yield return null: 每次调用这个语句,Unity 会创建一个新的迭代器状态机。当你执行协程时,如果你在协程中使用 yield return null,它会生成一个新的迭代器,这样会产生额外的内存分配。

  • new WaitForEndOfFrame(): 每次使用 new WaitForEndOfFrame() 都会创建一个新的 WaitForEndOfFrame 对象。这种频繁的对象创建会增加内存开销,并可能导致 GC 的触发。

但是注意,启动协程时会创建一个 Coroutine 对象,这本身会导致一次内存分配,进而可能引发垃圾回收(GC)。这是协程在 Unity 中的一个固有特性,无法完全避免。但是它还是解决了协程的主要痛点,毕竟一个项目启动的协程一般不会很多。如果你在意,可以选择使用 UniTask:
【推荐100个unity插件之33】比 Unity 自带协程更高效的异步处理方式,提供一个高性能和0GC的async/await异步方案——UniTask插件

封装协程工具类,避免 GC(垃圾回收)

提前new好协程所需要的WaitForEndOfFrame、WaitForFixedUpdate、WaitForFrameStruct类的对象,避免GC。

/// <summary>
/// 协程工具类,避免 GC(垃圾回收)
/// </summary>
public static class CoroutineTool
{
    // 定义一个结构体,用于表示等待一帧的状态
    private struct WaitForFrameStruct : IEnumerator
    {
        public object Current => null;

        public bool MoveNext() { return false; } // 一旦调用,立即返回 false,停止迭代

        public void Reset() { } // 重置方法,不做任何操作
    }

    // 预定义的等待结束帧对象,避免多次创建
    private static WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
    // 预定义的等待固定更新对象,避免多次创建
    private static WaitForFixedUpdate waitForFixedUpdate = new WaitForFixedUpdate();

    /// <summary>
    /// 获取等待结束帧的对象
    /// </summary>
    public static WaitForEndOfFrame WaitForEndOfFrame()
    {
        return waitForEndOfFrame;
    }

    /// <summary>
    /// 获取等待固定更新的对象
    /// </summary>
    public static WaitForFixedUpdate WaitForFixedUpdate()
    {
        return waitForFixedUpdate;
    }

    /// <summary>
    /// 等待指定时间(以秒为单位)
    /// </summary>
    /// <param name="time">等待的时间</param>
    public static IEnumerator WaitForSeconds(float time)
    {
        float currTime = 0;
        while (currTime < time)
        {
            currTime += Time.deltaTime;
            yield return new WaitForFrameStruct(); // 等待一帧
        }
    }

    /// <summary>
    /// 等待指定的实时时间(不受时间缩放影响)
    /// </summary>
    /// <param name="time">等待的时间</param>
    public static IEnumerator WaitForSecondsRealtime(float time)
    {
        float currTime = 0; // 当前经过的时间
        while (currTime < time) // 当经过的时间小于指定时间时
        {
            currTime += Time.unscaledDeltaTime; // 增加经过的时间(不受时间缩放影响)
            yield return new WaitForFrameStruct(); // 等待一帧
        }
    }

    /// <summary>
    /// 等待指定帧数
    /// </summary>
    /// <param name="count">等待的帧数,默认为1</param>
    public static IEnumerator WaitForFrame(int count = 1)
    {
        for (int i = 0; i < count; i++)
        {
            yield return new WaitForFrameStruct(); // 等待一帧
        }
    }
}

使用

1.使用默认方式使用协程

IEnumerator DelayedAction()
{
    yield return new WaitForEndOfFrame();// 等待到当前帧的结束(即所有渲染操作完成后)
    yield return new WaitForFixedUpdate();// 等待到下一个固定更新(适用于物理计算)
    yield return new WaitForSeconds(2.0f); // 等待2秒(游戏时间)
    yield return new WaitForSecondsRealtime(2.0f);// 等待2秒(现实时间,不受游戏时间缩放影响)
 	yield return null;// 等待一帧
}

2.使用自定义的 CoroutineTool 工具类来等待不同的时间

IEnumerator DelayedAction()
{
    yield return CoroutineTool.WaitForEndOfFrame(); // 等待到当前帧的结束
    yield return CoroutineTool.WaitForFixedUpdate(); // 等待到下一个固定更新
    yield return CoroutineTool.WaitForSeconds(2.0f); // 等待2秒(游戏时间)
    yield return CoroutineTool.WaitForSecondsRealtime(2.0f); // 等待2秒(现实时间,不受游戏时间缩放影响)
    yield return CoroutineTool.WaitForFrame(); // 等待一帧
    yield return CoroutineTool.WaitForFrame(3); // 等待3帧
}

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

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

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

相关文章

人物型Agent开发(文心智能体平台创作分享)

开发平台&#xff1a;文心智能体平台AgentBuilder | 想象即现实 目录 一、开发灵感 &#xff08;一&#xff09;打破刻板印象 &#xff08;二&#xff09;以古鉴今&#xff0c;探索人性与情感 二、角色分析与设定 &#xff08;一&#xff09;西门庆特质 &#xff08;二&a…

我的深度学习笔记

传统观念认为&#xff1a;在不考虑算力的情况下&#xff0c;网络越深&#xff0c;其准确率就越高&#xff0c;最直接的方法就是把网络设计的越深越好。 事实上&#xff1a;随着网络的层数不断加深&#xff0c;当达到一定的书目之后&#xff0c;训练精度和测试精度都有下降&…

第十三届蓝桥杯真题Java c组C.纸张尺寸(持续更新)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;蓝桥杯关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 【问题描述】 在 ISO 国际标准中定义了 A0 纸张的大小为 1189mm 841mm&#…

AI运用在营销领域的经典案例及解析

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 在前面一篇文章当中&#xff0c;我给大家介…

[Redis][典型运用][缓存]详细讲解

目录 0.什么是缓存&#xff1f;1.使用Redis作为缓存1.为什么用&#xff1f;2.如何用&#xff1f; 2.缓存的更新策略0.前言1.定期生成2.实时生成 3.缓存相关问题1.缓存预热(Cache Preheating)2.缓存穿透(Cache Penetration)3.缓存雪崩(Cache Avalanche)4.缓存击穿(Cache Breakdo…

一种多版本、多人并行开发GIT分支管理规范

首发公众号&#xff1a; 赵侠客 引言 作为开发者每天在写代码的同时也在写BUG&#xff0c;所以一方面需要开发新的需求&#xff0c;另一方面还要填自己以前挖的坑。目前主流程序员都在使用GIT来管理自己的代码&#xff0c;当GIT仓库有多人维护或者项目有多个版本同时迭代开发时…

c++进阶学习--------多态

前言 需要声明的&#xff0c;本节课件中的代码及解释都是在vs2022下的x86程序中&#xff0c;涉及的指针都是4bytes。 如果要其他平台下&#xff0c;部分代码需要改动。 比如&#xff1a;如果是x64程序&#xff0c;则需要考虑指针是8bytes问题等等 1. 多态的概念 1.1 概念 …

.NET内网实战:白名单文件反序列化执行命令

01阅读须知 此文所节选自小报童《.NET 内网实战攻防》专栏&#xff0c;主要内容有.NET在各个内网渗透阶段与Windows系统交互的方式和技巧&#xff0c;对内网和后渗透感兴趣的朋友们可以订阅该电子报刊&#xff0c;解锁更多的报刊内容。 02基本介绍 本文内容部分节选自小报童…

【易社保-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

Java泛型方法的定义和使用、泛型类、泛型接口、泛型方法、通配符、泛型的上界与下界

文章目录 一、包装类1.1、基本数据类型和对应的包装类1.2、自动装箱和自动拆箱 二、基本介绍2.1、泛型引入背景2.1、什么是泛型2.2、为什么使用泛型 三、常见泛型字母含义四、泛型的使用4.1、泛型类4.2、泛型接口4.3、泛型方法 五、泛型的继承5.1、泛型不具备继承性5.2、何为数…

【Python】递归

专栏文章索引&#xff1a;Python 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 文章内容改自&#xff1a;bilibili博主(又懂啦) 目录 一、递归函数 二、理解递归函数 一、递归函数 一个函数在其函数体内调用函数自身&#xff0c;这样的函数就称为递归函数。递归函数的…

每日一练 2024.9.29(2)

目录 解题思路与代码实现 题目分析 一、解题策略 关键步骤&#xff1a; 二、代码实现 三、代码解析 四、复杂度分析 五、运行示例 示例1&#xff1a; 示例2&#xff1a; 六、总结 解题思路与代码实现 题目分析 这道题目要求我们找到字符串列表 strs 中的相似字符组…

Arch - 架构安全性_验证(Verification)

文章目录 OverView导图1. 引言&#xff1a;数据验证的重要性概述2. 数据验证的基本概念3. 数据验证的层次前端验证后端验证 4. 数据验证的标准做法5. 自定义校验注解6. 校验结果的处理7. 性能考虑与副作用8. 小结 OverView 即使只限定在“软件架构设计”这个语境下&#xff0c…

物理学基础精解【40】

文章目录 矢量积矢量积&#xff08;又称叉积、外积&#xff09;的几何意义一、面积表示二、垂直性三、方向性四、应用实例五、数学表达 矢量积&#xff08;叉积&#xff09;的坐标表示法矢量积的坐标表示法的几何意义矢量积的性质矢量积的应用 矢量积&#xff08;又称叉积、外积…

Linux——k8s组件

kubernetes 使用1.31.1 版本搭建集群核心组件&#xff0c;选择flannel 网络插件为整体集群的运行提供网络通信功能。 flannel 网络插件 kube-flannel kube-flannel-ds-9fgml 1/1 Running 1 (18m ago) 2d21h kube-flannel kube-flannel-ds-ghwbq …

<<迷雾>> 第 3 章 怎样才能让机器做加法 示例电路

全加器示意图 info::操作说明 鼠标单击开关切换开合状态 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/cyjsjdmw-examples/assets/circuit/cyjsjdmw-ch03-01-full-adder.txt 原图 由3个全加器组成的3比特加法机 info::操作说明…

Linux——pod的调度

pod的调度 控制器: rc/rs 副本数量控制器 主要保证pod的数量符合管理员要求&#xff0c;并不会对pod进行额外的管理 以下三种控制器&#xff0c;本质上是服务控制器。具备以下特性&#xff1a; 副本数量的控制服务的滚动更新&#xff08;更新pod&#xff09;支持更新失…

基于springboot vue 投票系统设计与实现

博主介绍&#xff1a;专注于Java vue .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的…

基于51单片机的2路电压采集proteus仿真

地址&#xff1a;https://pan.baidu.com/s/1oNOJJv78ecfWZkdlMyhNVQ 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectron…

Linux:LCD驱动开发

目录 1.不同接口的LCD硬件操作原理 应用工程师眼中看到的LCD 1.1像素的颜色怎么表示 ​编辑 1.2怎么把颜色发给LCD 驱动工程师眼中看到的LCD 统一的LCD硬件模型 8080接口 TFTRGB接口 什么是MIPI Framebuffer驱动程序框架 怎么编写Framebuffer驱动框架 硬件LCD时序分析…