是一个高性能,0GC的async/await异步方案
协程缺点:
依赖monobehaviour
不能进行异常处理
方法返回值获取困难
c#原生Task:
优点:
不依赖monobehaviour
可以处理异常
缺点:
Task消耗大,设计跨线程操作
uniTask
优点:
继承c#的task优点
基于值类型解决方案,0GC
默认使用主协程
https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
延时操作:Delay DelayFrame Yield NextFrame WaitForEndOfFrame
等待操作: Wait Until Wait Until Value Changed
条件操作: When All When Any
异步委托生成UniTask及相关的封装: UniTask.Void UniTask.Defer UniTask.Lazy
取消:CancellationToken GetCancellationTokenOnDeatory()
异常处理:Try Catch SuppressCancellationThrow
超时处理:取消的变种,通过 CancellationTokenSouce.CancelAfterSlim(TimeSpan)设置超时并将CancellationToken 传递给异步方法
Forget()
事件处理:
1.异步事件 Lamaba 表达式注册 使用 UniTask.Action 或 UniTask.UnityAction
2.UGUI 事件转换为可等待事件
AsAsyncEnumerable
3.MonoBehaviour 消息事件都可以转换异步流
异步Linq
异步迭代器
响应式组件
协程和task
void Start()
{StartCoroutine(waitting());
}
Ienumerator waitting()
{yield return new waitForSeconds(2);
}
using System.Threading.Tasks;
using UnityEngine;public class Zero :MonoBehaviour
{async void Start(){await DealTest(); }async Task<int> DealTest(){Debug.Log(Time.time);await Task.Delay(100);Debug.Log(Time.time);return 1;}
}
延迟操作的API、Dealy方法、方法的重载及参数作用
UniTask.Delay 延迟秒
UniTask.DelayFrame 延迟帧
UniTask.NextFrame 等待一帧
UniTask.Yield 等待一帧,用于处理转回主线程用。yield之前再其他线程跑,yield之后回到主线程跑。
Time.timeScale=0F;
//等待时间,是否忽略时间缩放的影响(即便时间缩放为0但依然执行等待并向下执行,但是Time.time会受到影响),
await UniTask.Delay(1000,true);
WaitUntil等方法
WaitUntil
//知道达到------才执行
//等到指定对象的参数发生变化时 参数目标 判断方法的委托 如果返回值变化的话 就发生变化
//WaitUntilValueChanged
//等到指定对象的参数发生变化时,才完成。
using Cysharp.Threading.Tasks;
using UnityEngine;public class Two : MonoBehaviour
{public GameObject ball;void Start(){WaitUtilTest();WaitUntilValueTest();}void Update(){ball.transform.Translate(new Vector3(1,0,0)*Time.deltaTime);}async void WaitUtilTest(){await UniTask.WaitUntil(()=>isFarThanOne());//等到小球的x轴大于1后再执行方法的语句ball.GetComponent<Renderer>().material.color = Color.red;}public bool isFarThanOne(){if (ball.transform.position .x>1){return true;}return false;}//如果小球的x轴变化就输出“小球值的变化”async void WaitUntilValueTest(){await UniTask.WaitUntilValueChanged(ball.transform,x=>x.position);Debug.Log("小球值的变化");}
}
WhenAll方法
当两个小球都达到1的时候,就执行等待函数代码
using Cysharp.Threading.Tasks;
using UnityEngine;public class Three : MonoBehaviour
{public GameObject ball1;public GameObject ball2;void Start(){TestWhenAll();}void Update(){ball1.transform.Translate(new Vector3(1, 0, 0) * Time.deltaTime);ball2.transform.Translate(new Vector3(1, 0, 0) * Time.deltaTime * 0.5f);}async void TestWhenAll(){UniTask task1 = UniTask.WaitUntil(() => ball1.transform.position.x > 1);UniTask task2 = UniTask.WaitUntil(() => ball2.transform.position.x > 1);await UniTask.WhenAll(task1, task2);string str = $"ball1:{ball1.transform.position.x},ball2:{ball2.transform.position.x}";Debug.Log(str);}
}
WhenAny方法
只有其中一个任务完成了就执行等待的方法
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;public class Four : MonoBehaviour
{public Button btn1;public Button btn2;public bool isClick1;public bool isClick2;void Start(){btn1.onClick.AddListener(() => Click1());btn2.onClick.AddListener(() => Click2());AllBtnClick();}public void Click1(){isClick1 = true;}public void Click2(){isClick2 = true;}async void AllBtnClick(){UniTask task1 = UniTask.WaitUntil(() => isClick1);UniTask task2 = UniTask.WaitUntil(() => isClick2);await UniTask.WhenAny(task1, task2);//uniTask 不能await两次Debug.Log("11");}
}
Defer
UniTask.Void 参数异步委托 直接启动一个异步委托 不考虑其等待 无需加await
UniTask.Defer 用异步委托快速生成返回UniTask的异步方法,必须加await 才能执行
void Start
{UniTask.Void(async () =>{Debug.Log("start" + Time.frameCount);await UniTask.NextFrame();Debug.Log("end" + Time.frameCount);});
}
void Start()
{await UniTask.Defer(async () =>{Debug.Log("start" + Time.frameCount);await UniTask.NextFrame();Debug.Log("end" + Time.frameCount);});
}
协程与uniTask的转换
一些需要用到等待的unity对戏提供GetAwaiter()功能,从而拿到Awaiter对戏就可以进行await了。UniTask已经对各种各样的unity对戏进行了GetAwaiter的扩展。
1.Coroutine的等待及UniTask的转换
2.AsyncOperation的等待 如场景异步加载 资源异步加载 网络请求
3.UGUI的部分响应方法等待 如鼠标点击事件
4.MonoBehaviour的部分功能可以等待 如触发器
5.部分插件的扩展DOTween
Task -> UniTask : 使用AsUniTask
UniTask -> UniTask : 使用AsAsyncUnitUniTask
UniTask -> UniTask : 使用AsUniTask,这两者的转换是无消耗的
//动画序列
await transform.DOMoveX(2,10);
await transform.DOMoveX(5,20);
//并行 并传递cancellation用于取消
var ct=this.GetCancellationTokenOnDestroy();
await UniTask.WhenAll(transform.DoMoveX(10,3).withCancellation(ct),transform.DoScale(10,3).withCancellation()
);
using Cysharp.Threading.Tasks;
using System.Collections;
using UnityEngine;public class Six : MonoBehaviour
{async void Start(){StartCoroutine(CorotineTest());//实现协程等待await CorotineTest();}IEnumerator CorotineTest(){Debug.Log("start");yield return new WaitForSeconds(1);Debug.Log("end");}
}
using Cysharp.Threading.Tasks;
using System;
using System.Collections;using UnityEngine;public class Six : MonoBehaviour
{async void Start(){StartCoroutine(CorotineTest1());}IEnumerator CorotineTest1(){Debug.Log("start");//使用这个方法将UniTask组件转化成协程//如果你想将异步转换成协程,你可以使用.ToCoroutine(),如果你只想允许使用携程系统,这很有用。yield return UniTask.Delay(TimeSpan.FromSeconds(1)).ToCoroutine();Debug.Log("end");}
}