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("[协程加载] 资源加载失败!");
}
}
实现特点:
- 通过
yield return null
轮询加载状态 - 需要手动维护协程生命周期
- 每帧产生约24字节GC分配(来自IEnumerator装箱)
- 代码结构嵌套层级多
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加载] 资源加载失败!");
}
}
实现优势:
- 零GC分配:直接await异步操作,无需装箱
- 代码扁平化:消除嵌套回调
- 自动取消支持:通过CancellationToken实现安全中断
- 时序控制灵活:支持指定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