概述
入门系列中我们用协程(Coroutine)处理延时和异步操作。协程的局限性很明显:不能返回值、无法处理异常、不能在协程外等待它完成、每帧有 MoveNext 的装箱开销。
UniTask 是 Unity 专用的高性能异步框架,它实现了 C# 的 Task-like 模式,零 GC 分配,完全兼容 async/await 语法,并且深度集成 Unity 的生命周期。
1. 协程的痛点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| private IEnumerator LoadData() { var request = UnityWebRequest.Get("https://api.example.com/data"); yield return request.SendWebRequest(); }
private IEnumerator DoSomething() { yield return SomeOperation(); }
void SomeMethod() { StartCoroutine(MyCoroutine()); }
|
2. UniTask 基础
2.1 安装
通过 Package Manager 添加:
1
| https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
|
引入命名空间:
1
| using Cysharp.Threading.Tasks;
|
2.2 基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13
| private async UniTask LoadAndDisplayAsync() { var data = await FetchDataAsync(); Debug.Log("加载完成: {data}"); }
private async UniTask<string> FetchDataAsync() { await UniTask.Delay(1000); return "Hello UniTask"; }
|
2.3 替代常用协程 API
| 协程 |
UniTask |
yield return new WaitForSeconds(1) |
await UniTask.Delay(1000) |
yield return null |
await UniTask.Yield() |
yield return new WaitForEndOfFrame() |
await UniTask.WaitForEndOfFrame() |
yield return new WaitForFixedUpdate() |
await UniTask.WaitForFixedUpdate() |
yield return new WaitUntil(() => flag) |
await UniTask.WaitUntil(() => flag) |
yield return new WWW(url) |
await UnityWebRequest.Get(url).SendWebRequest() |
3. 取消操作(CancellationToken)
协程无法在启动后被外部取消(只能 StopCoroutine 或销毁物体)。UniTask 通过 CancellationToken 实现标准的取消流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| private CancellationTokenSource _cts;
private void Start() { _cts = new CancellationTokenSource(); StartLongOperation(_cts.Token).Forget(); }
private void OnDestroy() { _cts?.Cancel(); _cts?.Dispose(); }
private async UniTask StartLongOperation(CancellationToken token) { try { for (int i = 0; i < 100; i++) { token.ThrowIfCancellationRequested();
await UniTask.Delay(100, cancellationToken: token); Debug.Log("进度: {i}%"); } } catch (OperationCanceledException) { Debug.Log("操作被取消"); } }
|
3.1 自动绑定生命周期
1 2 3 4
| private async UniTaskVoid Start() { await SomeOperation(this.GetCancellationTokenOnDestroy()); }
|
UniTaskVoid 类似 async void,但不会吞异常(异常会触发 UniTaskScheduler 的全局异常处理)。
4. 并行与组合
4.1 等待多个任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private async UniTask LoadAllAsync() { var task1 = LoadTextureAsync("texture1"); var task2 = LoadTextureAsync("texture2"); var task3 = LoadAudioAsync("bgm");
await UniTask.WhenAll(task1, task2, task3); Debug.Log("所有资源加载完成"); }
private async UniTask<string> FetchFastestAsync() { var server1 = FetchFromServer("https://server1.example.com"); var server2 = FetchFromServer("https://server2.example.com");
var result = await UniTask.WhenAny(server1, server2); return result.result; }
|
4.2 超时控制
1 2 3 4 5 6 7 8 9 10 11 12 13
| private async UniTask<string> FetchWithTimeoutAsync(string url) { try { var task = UnityWebRequest.Get(url).SendWebRequest().ToUniTask(); return await task.Timeout(TimeSpan.FromSeconds(3)); } catch (TimeoutException) { Debug.LogWarning("请求超时,使用缓存数据"); return GetCachedData(); } }
|
5. 与 Unity 组件深度集成
5.1 等待异步操作
1 2 3 4 5 6
| await SceneManager.LoadSceneAsync("GameScene").ToUniTask();
await Resources.LoadAsync<GameObject>("Prefabs/Enemy").ToUniTask();
var handle = Addressables.LoadAssetAsync<GameObject>("Enemy"); await handle.ToUniTask();
|
5.2 等待事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private async UniTask WaitForPlayerClickAsync() { Debug.Log("等待玩家点击..."); await UniTask.WaitUntil(() => Input.GetMouseButtonDown(0)); Debug.Log("玩家点击了"); }
private async UniTask PlayAnimationAsync(Animator animator, string stateName) { animator.Play(stateName); await UniTask.WaitUntil(() => { var state = animator.GetCurrentAnimatorStateInfo(0); return state.IsName(stateName) && state.normalizedTime >= 1f; }); }
|
5.3 渐变补间
用 UniTask 实现简单的补间动画,无需 DOTween:
1 2 3 4 5 6 7 8 9 10 11 12 13
| private async UniTask FadeCanvasGroup(CanvasGroup group, float targetAlpha, float duration) { float startAlpha = group.alpha; float elapsed = 0f;
while (elapsed < duration) { elapsed += Time.deltaTime; group.alpha = Mathf.Lerp(startAlpha, targetAlpha, elapsed / duration); await UniTask.Yield(); } group.alpha = targetAlpha; }
|
6. 异步工厂与对象池
UniTask 和对象池的结合特别适合需要延迟创建的场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class AsyncBulletPool : MonoBehaviour { [SerializeField] private GameObject _bulletPrefab; private readonly Queue<Bullet> _pool = new(); private CancellationToken _ct;
private void Awake() { _ct = this.GetCancellationTokenOnDestroy(); }
public async UniTask<Bullet> SpawnAsync(Vector3 position, Quaternion rotation) { Bullet bullet;
if (_pool.Count > 0) { bullet = _pool.Dequeue(); bullet.gameObject.SetActive(true); } else { var handle = Addressables.InstantiateAsync(_bulletPrefab.name, position, rotation); var go = await handle.ToUniTask(cancellationToken: _ct); bullet = go.GetComponent<Bullet>(); }
bullet.transform.position = position; bullet.transform.rotation = rotation; await PlaySpawnEffectAsync(bullet.transform.position); return bullet; } }
|
7. 错误处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| private async UniTask SafeLoadAsync() { try { var data = await FetchDataAsync(); ProcessData(data); } catch (UnityWebRequestException ex) { Debug.LogError("网络请求失败: {ex.ResponseCode} - {ex.Message}"); await ShowErrorPanelAsync("网络错误,请重试"); } catch (OperationCanceledException) { Debug.Log("操作被用户取消"); } catch (Exception ex) { Debug.LogError("未知错误: {ex}"); await ShowErrorPanelAsync("发生未知错误"); } }
|
8. 性能对比
| 场景 |
协程 |
UniTask |
| 空循环 10000 次 |
~15ms |
~0.3ms |
| 每帧 GC 分配 |
是(enumerator 状态机) |
否(struct 状态机) |
| 返回值 |
不支持 |
支持 UniTask |
| 异常处理 |
无法 catch |
完整 try-catch |
| 取消支持 |
需额外实现 |
CancellationToken |
| 跨场景持久 |
停止 |
可控 |
| Unity 生命周期集成 |
原生 |
通过 GetCancellationTokenOnDestroy |
9. 迁移策略
不要一口气全部替换,按优先级逐步迁移:
- 所有
yield return new WaitForSeconds -> UniTask.Delay
- 所有
StartCoroutine / StopCoroutine -> async UniTask / CancellationToken
- 需要返回值的异步操作 ->
UniTask<T>
- 网络请求 + Addressables 加载 ->
ToUniTask()
- 需要超时控制的场景 ->
.Timeout()
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private IEnumerator Start() { yield return new WaitForSeconds(1); var req = UnityWebRequest.Get(url); yield return req.SendWebRequest(); }
private async UniTaskVoid Start() { await UniTask.Delay(1000); var req = await UnityWebRequest.Get(url).SendWebRequest().ToUniTask(); }
|
总结
UniTask 不是”另一个异步框架”,它是 Unity 中现代异步编程的基础设施:
- 零 GC 的 struct 状态机
- 完整的 async/await 支持
- CancellationToken 标准取消流程
- 深度 Unity 集成(生命周期、主线程调度)
- 性能比协程高 50 倍
下一篇将探讨 自定义编辑器扩展,教你构建提升开发效率的工具。