TIP原文章地址 本文章是原贴文章的简体中文加与加入个人理解的总结
在 Unity 开发中,异步处理是无法避免的,例如资源加载,网络请求或等待游戏流程事件。Coroutine 是 Unity 内置且最常见的方案,但实际项目中非常容易出现流程分散,可读性下降,以及性能受限等问题。
UniTask 的优势
高效轻量
UniTask 的设计目标时间少 GC 分配,解决 Unity 中过多的内存回收问题,相较于 Task 与 Coroutine,性能表现更加出色。
使用方便
基于 C# 的 async/await 语法,让异步程序像同步程序一样直观可读,可以摆脱回调地狱。
与 Unity 深度整合
内置了许多针对 Unity 的 API,例如等待帧结束,条件达成,场景加载等,完全满足日常的开发需求。
使用方式
基本结构
UniTask 的核心是 async/await。以下是一个简单的例子。
using Cysharp.Threading.Tasks;using UnityEngine;
public class UniTaskDemo : MonoBehaviour{ private async void Start() { await TaskExample(); // <-- Debug.Log("Task Finished"); }
private async UniTask TaskExample() { Debug.Log("Task Started"); await UniTask.Delay(2000); // Delay 2 seconds Debug.Log("Delay Finished"); }}
UniTask.Delay是常用的异步 API。UniTask是async支援的只要单位。
另外,也可以使用 UniTask.Create 创建一个 UniTask:
private async void Start(){ await UniTask.Create(async () => { Debug.Log("Task Started"); await UniTask.Delay(2000); // Delay 2 seconds Debug.Log("Delay Finished"); });
Debug.Log("Task Finished");}延迟控制
使用UniTask替代Coroutine的yield进行时间或帧数控制,例如:
await UniTask.Yield(); // replacement of yield return nullawait UniTask.Delay(TimeSpan.FromSeconds(2), ignoreTimeScale: false);await UniTask.DelayFrame(30);await UniTask.WaitForEndOfFrame();等待条件达成
WaitUntil可以等待玩家操作或某些条件成立,另外也有WhenAll,WhenAny可以使用。
await UniTask.WaitUntil(() => Input.GetKeyDown(KeyCode.Space));Debug.Log("Space key pressed");
await UniTask.WhenAll(Task1(), Task2());Debug.Log("All tasks completed");
await UniTask.WhenAny(Task1(), Task2());Debug.Log("One of the tasks completed");
private async UniTask Task1(){ await UniTask.Delay(1000); Debug.Log("Task 1 completed!");}
private async UniTask Task2(){ await UniTask.Delay(2000); Debug.Log("Task 2 completed!");}
整合 Unity API
如果希望 Unity API 可以使用 UnityTask 的功能,可以通过ToUniTask()将不同类型的异步操作转换到UniTask的生态中。
例如,同时加载多个资源并等待他们全部完成。
var (asset1, asset2) = await UniTask.WhenAll( Resources.LoadAsync<TextAsset>("Asset1").ToUniTask(), Resources.LoadAsync<TextAsset>("Asset2").ToUniTask());另外,ToUniTask可以搭配Progress使用,实时监控加载速度:
var progress = Progress.Create<float>(x => Debug.Log($"Progress: {x * 100}%"));var asset3 = await Resources .LoadAsync<TextAsset>("Asset3") .ToUniTask(progress);取消操作
搭配CancellationToken管理任务生命周期:
var cts = new CancellationTokenSource();
try{ await UniTask.Delay(5000, cancellationToken: cts.Token);}catch (OperationCanceledException){ Debug.Log("Cancelled!");}finally{ cts.Dispose();}在 Unity 2022.2 之后,你甚至可以在MonoBehaviour中使用DestoryCancellationToken:
var asset4 = await Resources .LoadAsync<TextAsset>("Asset4") .WithCancellation(this.GetCancellationTokenOnDestroy()); // <--超时任务
可设置任务的超时时间,避免长时间阻塞:
var timeout = TimeSpan.FromSeconds(3);await UniTask.Delay(5000).TimeoutWithoutException(timeout);原任务需要5s才能完成,超时时间只有3s,等待3s后,超时机制先结束等待;不会因为超时而抛出TimeoutException
与 UGUI 时间整合
将 Unity 的按钮事件与异步操作结合:
await button.OnClickAsync(); // Wait for button clickDebug.Log("Button clicked!");
await button.OnClickAsAsyncEnumerable() .Take(3) .ForEachAsync(_ => Debug.Log("Button clicked!"));
Debug.Log("Task completed!");
与 Unity 其他插件整合
UniTask 支持其他常用的插件,例如 Addressables,DOTween:
// Addressablesawait Addressables.LoadAssetAsync<GameObject>("MyPrefab").ToUniTask();
// DOTweenawait transform.DOMove(Vector3.zero, 2f).ToUniTask();无论你是刚接触异步编程的新手,还是经验丰富的老手,UniTask 都能带了全新的开发体验。