浅谈Unity资源异步加载和Coroutine的使用

2023-11-07

为了节省内存,游戏的一些资源往往需要在运行时(runtime)动态加载。如果资源本身加载比较耗时,采用同步方法会产生卡顿现象,对此的解决方法通常采用多线程或者使用引擎本身自带的异步加载方法。在Unity开发中,由于一些方法(如Resources.Load)本身不支持在其它线程调用,因此多线程的使用会受到限制;而Unity脚本API对许多加载方式都有相应的异步方法,因此我们需要对Unity异步加载方法的机制有一定的理解。此外,协同进程(Coroutine)常常和异步加载方法联合使用,这里顺便也研究一下。

1. Unity异步加载资源

Unity在runtime加载资源有Resources和AssetBundle两类方法,由于AssetBundle需要先进行打包,因此在Editor中进行开发测试阶段使用不太方便。这里介绍Resources.LoadAsync异步加载资源方法,先上代码和运行结果:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class tryLoadAsync : MonoBehaviour
{

    public static TerrainData td;
    public static GameObject terrain00;
    private ResourceRequest request;

    void Start()
    {
#if UNITY_EDITOR
        request = Resources.LoadAsync("tt/myTD00"); //调用异步加载方法
        Debug.Log("异步方法!");
#endif
    }

    void Update()
    {
        if (request != null)
        {
            if (request.isDone)
            {
                Debug.Log("异步加载完成了!");
                td = request.asset as TerrainData;
                if (!td)
                {
                    Debug.Log("加载terraindata失败!");
                }
                //创建地形
                terrain00 = Terrain.CreateTerrainGameObject(td);
                terrain00.transform.position = new Vector3(0, 0, 0);
                terrain00.GetComponent<Terrain>().Flush();

                request = null;
            }
            else
            {
                Debug.Log("已经过了一帧了!"); //异步方法至少需要一帧的时间才能生效!
            }
        }
    }
}

异步加载和同步加载的区别用另外一种概念描述就是“阻塞”。同步方法会阻塞在当前代码的执行,而其它部分(如UI)都在等着它结束调用,因此如果资源加载很耗时,那么就会出现“卡住了”的现象。异步加载则是非阻塞的,调用完异步方法后,代码继续执行,而加载工作由Unity在后台另开辟一个异步线程来进行。比如上面的代码中,在初始化Start方法中调用了Resources.LoadAsync异步方法加载一个TerrainData地形资源,该方法立即返回一个ResourceRequest类型的消息,之后继续执行下一条Debug.Log语句,而并不会等待加载的完成。

加载状态的查询有两种方法:一种是后面要介绍的yield return方式,另一种是这里采用的手动查询方式。我们在每帧运行的Update方法中通过ResourceRequestisDone成员查询异步加载是否完成了。但是从控制台窗口的输出来看,第一帧执行的是else分支,也就是在第一帧时异步加载还没有完成。这并不是因为我们的资源过大(事实上这个TerrainData资源也很小),而是异步方法的生效至少要一帧的时间。异步加载的这个特性是十分重要的,不仅对于Resources类的方法,对于AssetBundle也一样。比如,你要在Update中查询一些游戏状态以决定是否要加载新的资源,你编写了判断条件、加载代码,这都很很棒。然后你继续编写代码想让这些资源立即生效,然而抱歉,在这一帧你的这些代码就没有任何意义了,因为无论你怎么样查询加载状态,它都会告诉你异步加载还没有成功,因为它至少有一帧的延迟微笑这种情况是值得注意的。


2. Couroutine的使用

协同进程Coroutine并不是线程,它使用yield return语句强行将代码分为两部分执行,每一帧Unity都会查询一下yield返回之前的位置是否满足条件。如果满足那么继续运行协程剩余的代码,并且至少会推迟一帧。比如下面的代码和运行结果:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System.Threading;

public class tryCoroutine : MonoBehaviour
{

    public static TerrainData td;
    public static GameObject terrain00;

    private ResourceRequest request;
    // Use this for initialization
    void Start()
    {
#if UNITY_EDITOR
        StartCoroutine(LoadTerrain());
        Debug.Log("开始协同进程!");
#endif
    }

    IEnumerator LoadTerrain()
    {
        request = Resources.LoadAsync("tt/myTD00");
        yield return request;

        Debug.Log("异步加载完成了!");
        td = request.asset as TerrainData;
        if (!td)
        {
            Debug.Log("加载terraindata失败!");
        }
        terrain00 = Terrain.CreateTerrainGameObject(td);
        terrain00.transform.position = new Vector3(0, 0, 0);
        terrain00.GetComponent<Terrain>().Flush();
    }

    // Update is called once per frame
    void Update()
    {
        if (td == null)
            Debug.Log("等一等");
    }
}

程序实现的功能和上面一样,都是加载一个TerrainData资源并创建一个地形。该脚本创建了一个名为LoadTerrainCoroutine,注意返回值一定是IEnumerator类型。在第一次运行至yield return语句时,程序返回,执行该帧的Update方法;在下一帧,由于异步加载完成,程序回到yield return的位置,继续执行这个Coroutine剩余的代码。可见,Coroutine并不是一个线程,它仍然运行在主线程中。


参考文献:

1. AssetBundle官方教程:https://unity3d.com/cn/learn/tutorials/topics/best-practices/guide-assetbundles-and-resources

2. Unity Script API:https://docs.unity3d.com/ScriptReference/index.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

浅谈Unity资源异步加载和Coroutine的使用 的相关文章

  • 绿幕换背景、绿幕视频实时换背景

    PS 陆陆续续做绿幕抠图相关的工作也有2年之久了 一直研究普通摄像头下的绿幕抠图工作 这样的工作要比摄影棚下的难度要高很多 当然现在也出来很多的工具 抠图算法也越来越成熟 本人较懒 后面会一点点的把相关内容补齐 先上图 上面是效果 边缘做的

随机推荐

  • 卷积核(又叫filter,neuron),设计CNN layers的技巧

    loss entropy求导 为0 那么该怎样求导呢 并行计算 视频 https www bilibili com video BV1Ht411g7Ef p 13 CNN的术语 共享参数 第二种版本解释CNN 前一个64个filter 通过
  • freeswitch六、freeswitch会议功能

    freeswitch默认的会议号 FreeSwitch 默认支持会议功能 有如下特点 1 不需要创建一个会议室的操作 只需要通过 conference 拨码计划就可以实现 2 会议室不真正存在 直到有人呼入为止 3 会议功能很强大 能实现灵
  • 【AOSP】Settings应用界面逻辑

    源码参考 AOSPXRef 现象效果 调试UI显示 Settings应用子界面Activity绝大部分都是SubSetting 通过dumpsys指令查看当前活动 adb shell dumpsys activity activities
  • python自动化_检测系统的空文件夹

    一 空文件夹的判断 1 os listdir 函数 2 权限得注意 二 统计检测消耗时间 1 引入datetime日期库 2 扫描开始start time 扫描结束end time 3 因为权限的原因 所以使用了try import os
  • Matlab中使用Mex时遇到的问题及解决方法

    在Matlab命令行使用mex命令时出现错误 error Building MFC application with MD d CRT dll version requires MFC shared dll version Please d
  • 中国姓氏大全(常见508个,罕见740个)

    1 比较靠谱的资料 资料来源 百度百科 中国姓氏 常见姓氏 508个 赵 钱 孙 李 周 吴 郑 王 冯 陈 褚 卫 蒋 沈 韩 杨 朱 秦 尤 许 何 吕 施 张 孔 曹 严 华 金 魏 陶 姜 戚 谢 邹 喻 柏 水 窦 章 云 苏 潘
  • xml报文编写以及解析

    封装电子保单回执报文 Document document org dom4j DocumentHelper createDocument document setXMLEncoding UTF 8 Element root document
  • ChatGPT“保姆级教程”——手把手教你1分钟快速制作思维导图(Markmap/Xmind+Markdown)

    目录 前言 使用ChatGPT生成markdown格式主题 Markmap Markdown 使用Markmap生成思维导图 Xmind Markdown 使用Xmind生成思维导图 建议 其它资料下载 前言 思维导图是一种强大的工具 它可
  • hdu 1003 最大连续子序列和及起始位置 && hdu 1087 最大上升子序列和

    hdu 1003 题意 求最大连续子序列和及起始位置 对于动态规划问题要找出其子问题 考虑到dp的无后效性 dp i 表示以i为结尾的最大值 当dp i 1 gt 0时 以i 1为值对以i为结尾的值有贡献 否则起始位置变为自己 动态地更新最
  • [从零开始学DeepFaceLab-6]: 使用-命令行八大操作步骤-第3步:从目标视频中提取图片

    目录 总体流程 步骤3 从目标视频中提取图片 3 0 目标视频文件和大小的选择 3 1 命令 3 cut video drop video on me bat 可选
  • 三大主流软件负载均衡器对比(LVS、Nginx、HAproxy)

    资料来自网络 做了部分的补充说明 LVS 1 抗负载能力强 性能高 能达到F5的60 对内存和CPU资源消耗比较低 2 工作在网络4层 通过VRRP协议 仅作代理之用 具体的流量是由linux内核来处理 因此没有流量的产生 3 稳定 可靠性
  • vue生命周期 —— 模板编译

    Vue 的 template 是如何编译成真正的 HTML 并做到双向绑定等等特殊功能的呢 在这张图中 我们可以看到 Vue 的模板编译是在 mount 的过程中进行的 在 mount 的时候执行了 compile 这个方法来将 templ
  • Linux 根目录满了 linux根目录扩容方法 详解!!!

    CentOS 7根目录扩容方法 最近公司测试服务器根目录满了 便有同事网上找了教程进行扩容 但是由于找的教程不够严谨 导致扩容失败 还丢失了一部分文件 所以这里详细说明一下方法 方法流程说明 1 查看系统存储空间 看一下 home做在卷已用
  • 【angular】项目实践-表格显示

    介绍 前端中经常用到的组件就是表格了 下面简单介绍下表格的显示 HTML文件 div class container style margin bottom 10px width 95 div class row div div
  • SpringBoot -- 使用logback记录日志

    Logback介绍 Logback是由log4j创始人设计的另一个开源日志组件 官方网站 http logback qos ch Logback的内核重写了 在一些关键执行路径上性能提升10倍以上 而且logback不仅性能提升了 初始化内
  • vue v-for循环中如何给部分元素添加事件和样式

    vue中给循环元素统一添加事件和样式很简单 下面看下单独给某个循环出来的元素添加事件和样式如何实现 demo vue
  • IPsec ×××基本实验

    IPsec 基本实验 一 实验拓扑 二 实验原理 IKE概述 用IPsec保护一个IP包之前 必须先建立一个安全联盟 SA SA可以手动创建或者动态建立 Internet密钥交换 IKE 用于动态建立SA IKE的精髓 通过一系列数据的交换
  • windows 服务器 部署java项目

    第一步 下载软件 只下载我这里需要的软件 如有不同请自行百度 链接 https pan baidu com s 1pAWffZZvKW2B9tj3YEuHeA pwd rps4 提取码 rps4 第二步 配置软件环境变量 配置并安装jdk
  • Jmeter系列-测试计划详细介绍(3)

    测试计划的作用 测试计划描述了 Jmeter 在执行时 一系列的步骤 一个完整的测试计划包含了一个或多个 线程组 逻辑控制器 采样器 监听器 定时器 断言和配置元素 Jmeter原件和组件的介绍 基本元件的介绍 多个类似功能组件的 容器 类
  • 浅谈Unity资源异步加载和Coroutine的使用

    为了节省内存 游戏的一些资源往往需要在运行时 runtime 动态加载 如果资源本身加载比较耗时 采用同步方法会产生卡顿现象 对此的解决方法通常采用多线程或者使用引擎本身自带的异步加载方法 在Unity开发中 由于一些方法 如Resourc