8.UniTask资源异步加载

  1. 8.资源异步加载
    1. 8.1 知识点
      1. 异步加载资源示例
      2. 原生协程实现
      3. UniTask实现
      4. 进行测试
    2. 8.2 知识点代码
      1. Lesson08_资源异步加载.cs

8.资源异步加载


8.1 知识点

异步加载资源示例

本示例通过对比传统协程与UniTask两种异步加载方式,展示UniTask在资源加载场景中的优势。示例包含两个按钮分别触发不同加载方式,并显示加载结果。

原生协程实现

IEnumerator ResNormal()
{
    Debug.Log($"[协程加载] 开始加载资源,当前时间: {Time.time}");
    ResourceRequest res = Resources.LoadAsync<TextAsset>("Lesson08_资源异步加载文本");
    
    while (!res.isDone)
    {
        yield return null;
    }

    if (res.asset != null)
    {
        text1.text = ((TextAsset)res.asset).text;
        Debug.Log($"[协程加载] 资源加载完成,当前时间: {Time.time}");
    }
    else
    {
        Debug.LogError("[协程加载] 资源加载失败!");
    }
}

实现特点:

  1. 通过yield return null轮询加载状态
  2. 需要手动维护协程生命周期
  3. 每帧产生约24字节GC分配(来自IEnumerator装箱)
  4. 代码结构嵌套层级多

UniTask实现

async void ResUniTask()
{
    Debug.Log($"[UniTask加载] 开始加载资源,当前时间: {Time.time}");
    ResourceRequest res = Resources.LoadAsync<TextAsset>("Lesson08_资源异步加载文本");
    var asset = await res;

    if (asset != null)
    {
        text2.text = ((TextAsset)asset).text;
        Debug.Log($"[UniTask加载] 资源加载完成,当前时间: {Time.time}");
    }
    else
    {
        Debug.LogError("[UniTask加载] 资源加载失败!");
    }
}

实现优势:

  1. 零GC分配:直接await异步操作,无需装箱
  2. 代码扁平化:消除嵌套回调
  3. 自动取消支持:通过CancellationToken实现安全中断
  4. 时序控制灵活:支持指定PlayerLoop阶段

进行测试

using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
using System.Collections;

/// <summary>
/// 资源异步加载测试示例
/// 
/// 为什么需要 UniTask?
/// 1. 原生 Task 太重,和 Unity 的单线程模型不兼容。
/// 2. UniTask 不依赖线程,也不使用 SynchronizationContext/ExecutionContext,
///    而是由 Unity 引擎层自动调度,性能更高,分配更低,并与 Unity 完全兼容。
/// 3. 使用 using Cysharp.Threading.Tasks; 后,您可以对 AsyncOperation、
///    ResourceRequest、AssetBundleRequest、AssetBundleCreateRequest、
///    UnityWebRequestAsyncOperation、AsyncGPUReadbackRequest、IEnumerator 等异步操作进行 await。
/// 
/// 本示例对比了传统协程和 UniTask 两种异步资源加载方式。
/// </summary>
public class Lesson08_资源异步加载 : MonoBehaviour
{
    public Button btn1; // 使用协程加载资源的按钮
    public Text text1; // 显示协程加载结果的文本

    public Button btn2; // 使用 UniTask 加载资源的按钮
    public Text text2; // 显示 UniTask 加载结果的文本


    void Start()
    {
        // 注册按钮点击事件
        btn1.onClick.AddListener(() => StartCoroutine(ResNormal()));
        btn2.onClick.AddListener(() => ResUniTask());
    }

    /// <summary>
    /// 使用原生协程进行资源异步加载
    /// </summary>
    /// <returns>IEnumerator</returns>
    IEnumerator ResNormal()
    {
        Debug.Log($"[协程加载] 开始加载资源,当前时间: {Time.time}");
        // 开始异步加载资源(资源名称为 "1",类型为 TextAsset)
        ResourceRequest res = Resources.LoadAsync<TextAsset>("Lesson08_资源异步加载文本");

        // 等待资源加载完成
        while (!res.isDone)
        {
            yield return null;
        }

        // 加载完成后判断是否成功
        if (res.asset != null)
        {
            text1.text = ((TextAsset)res.asset).text;
            Debug.Log($"[协程加载] 资源加载完成,当前时间: {Time.time}");
        }
        else
        {
            Debug.LogError("[协程加载] 资源加载失败!");
        }
    }

    /// <summary>
    /// 使用 UniTask 进行资源异步加载
    /// </summary>
    async void ResUniTask()
    {
        Debug.Log($"[UniTask加载] 开始加载资源,当前时间: {Time.time}");
        // 开始异步加载资源(资源名称为 "1",类型为 TextAsset)
        ResourceRequest res = Resources.LoadAsync<TextAsset>("Lesson08_资源异步加载文本");
        // 直接使用 await 等待资源加载完成(UniTask 已为 ResourceRequest 提供了扩展)
        var asset = await res;

        // 加载完成后判断是否成功
        if (asset != null)
        {
            text2.text = ((TextAsset)asset).text;
            Debug.Log($"[UniTask加载] 资源加载完成,当前时间: {Time.time}");
        }
        else
        {
            Debug.LogError("[UniTask加载] 资源加载失败!");
        }
    }
}


8.2 知识点代码

Lesson08_资源异步加载.cs

using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
using System.Collections;

/// <summary>
/// 资源异步加载测试示例
/// 
/// 为什么需要 UniTask?
/// 1. 原生 Task 太重,和 Unity 的单线程模型不兼容。
/// 2. UniTask 不依赖线程,也不使用 SynchronizationContext/ExecutionContext,
///    而是由 Unity 引擎层自动调度,性能更高,分配更低,并与 Unity 完全兼容。
/// 3. 使用 using Cysharp.Threading.Tasks; 后,您可以对 AsyncOperation、
///    ResourceRequest、AssetBundleRequest、AssetBundleCreateRequest、
///    UnityWebRequestAsyncOperation、AsyncGPUReadbackRequest、IEnumerator 等异步操作进行 await。
/// 
/// 本示例对比了传统协程和 UniTask 两种异步资源加载方式。
/// </summary>
public class Lesson08_资源异步加载 : MonoBehaviour
{
    public Button btn1; // 使用协程加载资源的按钮
    public Text text1; // 显示协程加载结果的文本

    public Button btn2; // 使用 UniTask 加载资源的按钮
    public Text text2; // 显示 UniTask 加载结果的文本


    void Start()
    {
        // 注册按钮点击事件
        btn1.onClick.AddListener(() => StartCoroutine(ResNormal()));
        btn2.onClick.AddListener(() => ResUniTask());
    }

    /// <summary>
    /// 使用原生协程进行资源异步加载
    /// </summary>
    /// <returns>IEnumerator</returns>
    IEnumerator ResNormal()
    {
        Debug.Log($"[协程加载] 开始加载资源,当前时间: {Time.time}");
        // 开始异步加载资源(资源名称为 "1",类型为 TextAsset)
        ResourceRequest res = Resources.LoadAsync<TextAsset>("Lesson08_资源异步加载文本");

        // 等待资源加载完成
        while (!res.isDone)
        {
            yield return null;
        }

        // 加载完成后判断是否成功
        if (res.asset != null)
        {
            text1.text = ((TextAsset)res.asset).text;
            Debug.Log($"[协程加载] 资源加载完成,当前时间: {Time.time}");
        }
        else
        {
            Debug.LogError("[协程加载] 资源加载失败!");
        }
    }

    /// <summary>
    /// 使用 UniTask 进行资源异步加载
    /// </summary>
    async void ResUniTask()
    {
        Debug.Log($"[UniTask加载] 开始加载资源,当前时间: {Time.time}");
        // 开始异步加载资源(资源名称为 "1",类型为 TextAsset)
        ResourceRequest res = Resources.LoadAsync<TextAsset>("Lesson08_资源异步加载文本");
        // 直接使用 await 等待资源加载完成(UniTask 已为 ResourceRequest 提供了扩展)
        var asset = await res;

        // 加载完成后判断是否成功
        if (asset != null)
        {
            text2.text = ((TextAsset)asset).text;
            Debug.Log($"[UniTask加载] 资源加载完成,当前时间: {Time.time}");
        }
        else
        {
            Debug.LogError("[UniTask加载] 资源加载失败!");
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏