Unity异步化

UniTask

链接:UniTask/src/UniTask/Assets/Plugins/UniTask at master · Cysharp/UniTask · GitHub

特性传统 TaskUniTask
GC 分配每次 await 产生 GC零 GC
时间系统依赖系统时钟自动同步 Time.timeScale
线程安全需手动回主线程默认在主线程执行
性能较慢快 2-5 倍

向上优化Task,向下兼容协程。 

命名空间

using Cysharp.Threading.Tasks;

与原生Task的API对比

.NET 类型UniTask 类型
IProgress<T>---
CancellationToken---
CancellationTokenSource---
Task/ ValueTaskUniTask
Task<T>/ ValueTask<T>UniTask<T>
async voidasync UniTaskVoid
+= async () => { }UniTask.Void, UniTask.Action, UniTask.UnityAction
---UniTaskCompletionSource
TaskCompletionSource<T>UniTaskCompletionSource<T>/ AutoResetUniTaskCompletionSource<T>
ManualResetValueTaskSourceCore<T>UniTaskCompletionSourceCore<T>
IValueTaskSourceIUniTaskSource
IValueTaskSource<T>IUniTaskSource<T>
ValueTask.IsCompletedUniTask.Status.IsCompleted()
ValueTask<T>.IsCompletedUniTask<T>.Status.IsCompleted()
new Progress<T>Progress.Create<T>
CancellationToken.Register(UnsafeRegister)CancellationToken.RegisterWithoutCaptureExecutionContext
CancellationTokenSource.CancelAfterCancellationTokenSource.CancelAfterSlim
Channel.CreateUnbounded<T>(false){ SingleReader = true}Channel.CreateSingleConsumerUnbounded<T>
IAsyncEnumerable<T>IUniTaskAsyncEnumerable<T>
IAsyncEnumerator<T>IUniTaskAsyncEnumerator<T>
IAsyncDisposableIUniTaskAsyncDisposable
Task.DelayUniTask.Delay
Task.YieldUniTask.Yield
Task.Run

UniTask.RunOnThreadPool

(不推荐,考虑是否能用UniTask.Create)

---UniTask.Create
---UniTask.Defer
---UniTask.Lazy
Task.WhenAllUniTask.WhenAll
Task.WhenAnyUniTask.WhenAny
Task.CompletedTaskUniTask.CompletedTask
Task.FromExceptionUniTask.FromException
Task.FromResultUniTask.FromResult
Task.FromCanceledUniTask.FromCanceled
Task.ContinueWithUniTask.ContinueWith
TaskScheduler.UnobservedTaskExceptionUniTaskScheduler.UnobservedTaskException

常用API

UniTask.Create

创建:该方法被调用的时候,立即创建一个新的UniTask

执行:UniTask在Create()被调用的那一刻就开始执行了

UniTask task = UniTask.Create(
  async ()=> 
  {
    Debug.Log("Create");
    await UniTask.Delay(1000);
    Debug.Log("Complete");
  });

UniTask.Defer

创建:将UniTask创建延迟到await的时机

执行:只能await一次,但比Lazy轻量

UniTask task = UniTask.Defer(
    async () => 
    {
       Debug.Log("Defer");
       await UniTask.Delay(1000);
       Debug.Log("Complete");
    }
);
await defer;

UniTask.Lazy

创建:将UniTask创建延迟到await的时机

执行:创建AsyncLazy,AsyncLazy.Task可以被await任意次数,比Defer的成本更高

AsyncLazy asyncLazy = UniTask.Lazy(
  async () =>
  {
    Debug.Log("AsyncLazy");
    await UniTask.Delay(1000);
    Debug.Log("Complete");
  }
);
await asyncLazy.Task;
await asyncLazy.Task;

UniTask与UniTaskVoid

场景特征使用 UniTask使用 UniTaskVoid
需要等待结果✅ (如 await GetPlayerData())
需要返回值✅ (如 return score)
UI 事件触发✅ (避免 async void 风险)
长时间后台任务✅ (结合 CancellationToken)✅ (需显式绑定生命周期)
高频触发(如每帧)❌ (会产生 Task 实例)✅ (零 GC 压力)

 DOTween UniTask化

 对Tween或Sequence动画可进行异步等待

public static class DOTweenExtensions {
    public static UniTask AwaitForComplete(this Tween tween, CancellationToken cancellationToken = default) {
        var tcs = new UniTaskCompletionSource();
        try {
            tween.OnComplete(() => tcs.TrySetResult());
            tween.OnKill(() => tcs.TrySetCanceled());
            cancellationToken.Register(() => tween.Kill());
        }
        catch (Exception e) {
            Debug.LogError($"Tween AwaitForComplete Error: {e}");
            tcs.TrySetException(new Exception($"DOTween AwaitForComplete Error: {e}"));
        }
        return tcs.Task;
    }
}

// 使用方法:
    try {
        await sequence.AwaitForComplete(cancelToken);
    }
    catch (OperationCanceledException) {
        Debug.Log("Tween Canceled");
    }
    catch (Exception ex) {
        Debug.LogError($"Tween Get Error: {ex}");
    }

Spine UniTask化

仅针对最常用的Spine.AnimationState.SetAnimation方法进行UniTask化。

该方法对循环动画的处理可自行定义。

本代码仅考虑了单轨道动画,对多轨道动画的实际表现未知。

public static class SpineExtensions {
    public static UniTask AwaitForComplete(this TrackEntry entry, Spine.AnimationState state = null, CancellationToken cancellationToken = default) {
        var tcs = new UniTaskCompletionSource();
        try {
            if (!entry.Loop) {
                entry.Complete += _ => tcs.TrySetResult();
                entry.End += _ => tcs.TrySetCanceled();
            }
            else {
                entry.End += _ => tcs.TrySetResult();
            }

            if (cancellationToken != default && state != null) {
                cancellationToken.Register(() => state.ClearTrack(entry.TrackIndex));
            }
        }
        catch (Exception e) {
            Debug.LogError($"TrackEntry AwaitForComplete Error: {e}");
            tcs.TrySetException(new Exception($"Spine AwaitForComplete Error: {e}"));
        }
        return tcs.Task;
    }
}

// 使用方法:
    try {
        TrackEntry entry = skeleton_animation.state.SetAnimation(0, animationName, loop);
        await entry.AwaitForComplete(skeleton_animation.state, cancelToken);
    }
    catch (OperationCanceledException) {
        Debug.Log("Animation Canceled");
    }
    catch (Exception ex) {
        Debug.LogError($"Animation Get Error: {ex}");
    }

Task UniTask化 

*.AsUniTask()

参考文章

https://zhuanlan.zhihu.com/p/572670728https://zhuanlan.zhihu.com/p/572670728spine-unity 运行时文档https://zh.esotericsoftware.com/spine-unity

### 实现异步加载资源的最佳实践 在 Unity 中,为了防止阻塞主线程并保持游戏流畅运行,推荐采用异步方式来加载资源。这可以通过多种方法达成。 #### 使用 `Addressable Asset System` `Addressable Asset System` 是一种更现代的方式来进行资产管理和异步加载[^1]。它提供了灵活性以及性能优化选项,适合大型项目使用。要利用此功能模块,需先安装 Addressables 包并通过特定 API 来请求所需资源。 对于简单的场景下,如果不想引入额外依赖,则可以考虑基于内置的 `Resources.LoadAsync<T>()` 方法实现基本需求。这种方法虽然不如前者强大,但对于小型应用来说已经足够有效。 下面是一个具体的例子展示如何运用协程配合 `LoadAssetAsync()` 函数执行异步加载: ```csharp using UnityEngine; using UnityEngine.ResourceManagement.AsyncOperations; public class AsyncResourceLoader : MonoBehaviour { private async void LoadModel(string path){ var operation = Resources.LoadAsync<GameObject>(path); while (!operation.isDone) await Task.Yield(); GameObject modelPrefab = operation.asset as GameObject; Instantiate(modelPrefab, transform.position, Quaternion.identity); Debug.Log($"Loaded Model from {path}"); } } ``` 上述代码片段展示了当调用 `LoadModel` 方法时会启动一个异步过程去获取指定路径下的模型预制件,并将其实例化到当前物体位置上;期间不会影响其他更新循环中的任务正常运作。 此外,在某些情况下可能还需要监听加载进度或是设置超时机制以增强用户体验和稳定性。此时可借助回调函数或者观察者模式进一步扩展逻辑结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值