Unitask学习记录-附工程文件

工程文件下载

using Cysharp.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;public class UniTaskTest : MonoBehaviour
{private UniTaskSys uniTaskSys;public Image img_Test;//百度一张图片:右击,在新标签页中打开图像,复制urlprivate string url;private bool isO_Down;private bool isP_Down;private void Awake(){uniTaskSys = new UniTaskSys();url = "https://n.sinaimg.cn/sinacn18/600/w1920h1080/20180508/2a45-haichqy4087272.jpg";}private void Start(){//AsyncLoadTextAsset("1");LoadScene("Demo");//AsyncWebRequestTexture(url);//uniTaskSys.DelayTest(this);WhenAllTest();WhenAnyTest();}private void Update(){if (Input.GetKeyDown(KeyCode.O)){isO_Down = true;}if (Input.GetKeyDown(KeyCode.P)){isP_Down = true;}}private async void AsyncLoadTextAsset(string path){TextAsset textAsset = await uniTaskSys.AsyncLoad<TextAsset>(path) as TextAsset;Debug.Log(textAsset.text);}private void LoadScene(string sceneName){uniTaskSys.AsyncLoadScene(sceneName);}public async void AsyncWebRequestTexture(string url){var webRequest = UnityWebRequestTexture.GetTexture(url);var asyncOperation = await webRequest.SendWebRequest();Texture2D texture2D = ((DownloadHandlerTexture)asyncOperation.downloadHandler).texture;img_Test.sprite = Sprite.Create(texture2D, new Rect(0, 0, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f));img_Test.SetNativeSize();}/// <summary>/// 当两个条件都符合了才执行后面操作/// </summary>public async void WhenAllTest(){var firstOperation = UniTask.WaitUntil(() => isO_Down);var secondOperation = UniTask.WaitUntil(() => isP_Down);await UniTask.WhenAll(firstOperation, secondOperation);// 注意,whenAll可以用于平行执行多个资源的读取,非常有用!// var (a, b, c) = await UniTask.WhenAll(//LoadAsSprite("foo"),//LoadAsSprite("bar"),//LoadAsSprite("baz"));Debug.Log("条件01、02都符合条件了");}/// <summary>/// 当其中一个按钮点击了就执行后面操作/// </summary>private async void WhenAnyTest(){var firstOperation = UniTask.WaitUntil(() => isO_Down);var secondOperation = UniTask.WaitUntil(() => isP_Down);await UniTask.WhenAny(firstOperation, secondOperation);Debug.Log(isO_Down ? "O被按下" : "P被按下");}}public class UniTaskSys
{public int count = 0;public async UniTask<Object> AsyncLoad<T>(string path) where T : Object{// 加载资源var asyncOperation = Resources.LoadAsync<T>(path);return (await asyncOperation);}public async void AsyncLoadScene(string sceneName){// 加载场景await SceneManager.LoadSceneAsync(sceneName).ToUniTask(Progress.Create<float>((p) =>{Debug.Log($"加载进度为{p * 100:F2}%");}));}public async void DelayTest(MonoBehaviour mono = null){Debug.Log(Time.realtimeSinceStartup);//性能最好,可以设置等待时机,PlayerLoopTiming 对应Unity中playerloop的更新时机//await UniTask.Yield(PlayerLoopTiming.LastUpdate);//等待1秒,类似 yield return new WaitForSeconds(1),可以设置 ignoreTimeScale//await UniTask.Delay(System.TimeSpan.FromSeconds(1), false);//执行在下一帧的update之后,类似 yield return null,和 UniTask.Yield() 效果一样//await UniTask.NextFrame();//这一帧的最后,类似 yield return new WaitForEndOfFrame(),this是一个MonoBehaviour//await UniTask.WaitForEndOfFrame(mono);//类似 yield return new WaitForFixedUpdate,和 await UniTask.Yield(PlayerLoopTiming.FixedUpdate)效果一样//await UniTask.WaitForFixedUpdate();//延迟5帧//await UniTask.DelayFrame(5);//类似 yield return new WaitUntil(() => count > 10),当count > 10时才执行后面逻辑await UniTask.WaitUntil(() => count > 10);Debug.Log(Time.realtimeSinceStartup);}}
using Cysharp.Threading.Tasks;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;public class UniTaskCancel : MonoBehaviour
{public Transform FirstTransform;public Transform SecondTransform;public Button FirstRunButton;public Button SecondRunButton;public Button FirstCancelButton;public Button SecondCancelButton;//做取消时需要创建这个对象private CancellationTokenSource _firstCancelToken;private CancellationTokenSource _secondCancelToken;private CancellationTokenSource _linkedCancelToken;private void Start(){FirstRunButton.onClick.AddListener(OnClickFirstMove);SecondRunButton.onClick.AddListener(OnClickSecondMove);FirstCancelButton.onClick.AddListener(OnClickFirstCancel);SecondCancelButton.onClick.AddListener(OnClickSecondCancel);_firstCancelToken = new CancellationTokenSource();// 注意这里可以直接先行设置多久以后取消// _firstCancelToken = new CancellationTokenSource(TimeSpan.FromSeconds(1.5f));_secondCancelToken = new CancellationTokenSource();//用两个token创建新的linkedCancelToken,当其中一个取消后,linkedCancelToken也会取消,_linkedCancelToken =CancellationTokenSource.CreateLinkedTokenSource(_firstCancelToken.Token, _secondCancelToken.Token);}/// <summary>/// 移动first,使用try catch监听取消信号/// </summary>private async void OnClickFirstMove(){try{await MoveTransform(FirstTransform, _firstCancelToken.Token);}catch (OperationCanceledException e){//发出取消信号,这里会抛异常Debug.Log("first已经被取消");}}/// <summary>/// 移动second,忽略异常的抛出,返回一个值元组,这种方式性能更好/// </summary>private async void OnClickSecondMove(){//第一个参数表示是否取消,第二个参数时await的返回值var (cancelled, _) = await MoveTransform(SecondTransform, _secondCancelToken.Token).SuppressCancellationThrow();// 使用LinkedToken,当first取消后,second也会取消// var (cancelled, _) = await MoveTransform(SecondTransform, _linkedCancelToken.Token).SuppressCancellationThrow();if (cancelled){Debug.Log("second已经被取消");}}private async UniTask<int> MoveTransform(Transform tf, CancellationToken cancellationToken){float totalTime = 10;float timeElapsed = 0;while (timeElapsed <= totalTime){timeElapsed += Time.deltaTime;await UniTask.NextFrame(cancellationToken);tf.transform.localPosition += Vector3.right * Time.deltaTime * 3;}return 0;}/// <summary>/// 取消first移动,Token使用后就不能再次使用,得创建新的Token/// </summary>private void OnClickFirstCancel(){_firstCancelToken.Cancel();_firstCancelToken.Dispose();_firstCancelToken = new CancellationTokenSource();_linkedCancelToken =CancellationTokenSource.CreateLinkedTokenSource(_firstCancelToken.Token, _secondCancelToken.Token);}private void OnClickSecondCancel(){_secondCancelToken.Cancel();_secondCancelToken.Dispose();_secondCancelToken = new CancellationTokenSource();_linkedCancelToken =CancellationTokenSource.CreateLinkedTokenSource(_firstCancelToken.Token, _secondCancelToken.Token);}private void OnDestroy(){_firstCancelToken.Dispose();_secondCancelToken.Dispose();_linkedCancelToken.Dispose();}
}
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using UnityEngine;
using UnityEngine.UI;/// <summary>
/// UI事件模板
/// </summary>
public class UIEventsSample : MonoBehaviour
{public Transform trans_Sphere;public Button SphereButton;public Button DoubleClickButton;public Button CoolDownButton;public float DoubleClickCheckTime = 0.5f;public float CooldownTime = 3f;void Start(){CheckSphereClick(SphereButton.GetCancellationTokenOnDestroy()).Forget();CheckDoubleClickButton(DoubleClickButton, this.GetCancellationTokenOnDestroy()).Forget();CheckCooldownClickButton(this.GetCancellationTokenOnDestroy()).Forget();}/// <summary>/// 球体连点/// </summary>private async UniTaskVoid CheckSphereClick(CancellationToken token){//将按钮的点击转换为异步可迭代器var asyncEnumerable = SphereButton.OnClickAsAsyncEnumerable();//ForEachAsync处理每一次点击时的操作,index表示第几次点击,Take(3)表示只处理前三次点击await asyncEnumerable.Take(3).ForEachAsync((_, index) =>{if (token.IsCancellationRequested) return;if (index == 0){//第一次点击,放大SphereTweenScale(2, trans_Sphere.localScale.x, 5, token).Forget();}else if (index == 1){//第二次点击,缩小SphereTweenScale(2, trans_Sphere.localScale.x, 2, token).Forget();}else if (index == 2){//第三次点击销毁}}, token);GameObject.Destroy(trans_Sphere.gameObject);//三次点击后,await完成,可以进行后面的逻辑Debug.LogError("done");}private async UniTaskVoid SphereTweenScale(float totalTime, float from, float to, CancellationToken token){var trans = trans_Sphere;float time = 0;while (time < totalTime){time += Time.deltaTime;if (trans) {trans.localScale = (from + (time / totalTime) * (to - from)) * Vector3.one;}await UniTask.Yield(PlayerLoopTiming.Update, token);}}/// <summary>/// 双击按钮/// </summary>private async UniTaskVoid CheckDoubleClickButton(Button button, CancellationToken token){while (true){//将点击转换为异步的UniTask,然后等待第一次点击var clickAsync = button.OnClickAsync(token);await clickAsync;Debug.Log($"按钮被第一次点击");var secondClickAsync = button.OnClickAsync(token);//第二次点击和等待时间谁先到,WhenAny返回那个先执行int resultIndex = await UniTask.WhenAny(secondClickAsync,UniTask.Delay(TimeSpan.FromSeconds(DoubleClickCheckTime), cancellationToken: token));Debug.Log(resultIndex == 0 ? $"按钮被双击了" : $"超时,按钮算单次点击");}}/// <summary>/// 按钮冷却时间/// </summary>private async UniTaskVoid CheckCooldownClickButton(CancellationToken token){var asyncEnumerable = CoolDownButton.OnClickAsAsyncEnumerable();await asyncEnumerable.ForEachAwaitAsync(async (_) =>{Debug.Log("被点击了,冷却中……");//Delay过程中不会再响应点击操作await UniTask.Delay(TimeSpan.FromSeconds(CooldownTime), cancellationToken: token);Debug.Log("冷却好了,可以点了……");}, cancellationToken: token);}}
using Cysharp.Threading.Tasks;
using System;
using System.Threading;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;/// <summary>
/// 超时操作
/// </summary>
public class TimeoutTest : MonoBehaviour
{public Button TestButton;private void Start(){//使用UniTask.UnityAction包装了OnClickTestTestButton.onClick.AddListener(UniTask.UnityAction(OnClickTest));}private async UniTaskVoid OnClickTest(){var res = await GetRequest("https://www.baidu.com/", 2f);//var res = await GetRequest("https://www.google.com/", 2f);Debug.LogError(res);}private async UniTask<string> GetRequest(string url, float timeout){//这个token会在timeout之后发出取消信号var cts = new CancellationTokenSource();cts.CancelAfterSlim(TimeSpan.FromSeconds(timeout));var (failed, result) = await UnityWebRequest.Get(url).SendWebRequest().WithCancellation(cts.Token).SuppressCancellationThrow();if (!failed){//成功了返回网页内容的开头return result.downloadHandler.text.Substring(0, 100);}return "超时";}
}
using Cysharp.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;/// <summary>
/// 切换到主线程
/// </summary>
public class ThreadSample : MonoBehaviour
{public Button btn_StandardRun;public Button btn_YieldRun;private void Start(){btn_StandardRun.onClick.AddListener(UniTask.UnityAction(OnClickStandardRun));btn_YieldRun.onClick.AddListener(UniTask.UnityAction(OnClickYieldRun));}public async UniTaskVoid OnClickStandardRun(){int result = 0;//切换到其他线程await UniTask.RunOnThreadPool(()=> { result = 1; });//切换到主线程await UniTask.SwitchToMainThread();Debug.Log($"计算结束,当前结果为:{result}");}/// <summary>/// 线程中读取文件/// </summary>private async UniTaskVoid OnClickYieldRun(){string fileName = Application.streamingAssetsPath + "/Test.txt";await UniTask.SwitchToThreadPool();string fileContent = await File.ReadAllTextAsync(fileName);//调用 UniTask.Yield 会自动切换会主线程await UniTask.Yield(PlayerLoopTiming.Update);Debug.LogError(fileContent);}}
using Cysharp.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class ForgetSample : MonoBehaviour
{public Button StartButton;public GameObject Target;public const float G = 9.8f;public float FallTime = 0.5f;private void Start(){StartButton.onClick.AddListener(OnClickStart);}/// <summary>/// 同步方法中调用异步方法/// </summary>private void OnClickStart(){//不需要等待时候就调用ForgetFallTarget(Target.transform).Forget();}/// <summary>/// 使目标掉落,async UniTaskVoid是async UniTask的轻量级版本/// </summary>private async UniTaskVoid FallTarget(Transform targetTrans){Vector3 startPosition = targetTrans.position;float fallTime = 1f;float elapsedTime = 0;while (elapsedTime <= fallTime){elapsedTime += Time.deltaTime;float fallY = 0.5f * G * elapsedTime * elapsedTime;targetTrans.position = startPosition + Vector3.down * fallY;//GetCancellationTokenOnDestroy 表示获取一个依赖对象生命周期的Cancel句柄,//当对象被销毁时,将会调用这个Cancel句柄,从而实现取消的功能await UniTask.Yield(this.GetCancellationTokenOnDestroy());}}}
using Cysharp.Threading.Tasks;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class CallbackSample : MonoBehaviour
{public Button CallbackButton;public GameObject Target;public const float G = 9.8f;public event EventHandler OneSecondHandler;private void Start(){CallbackButton.onClick.AddListener(UniTask.UnityAction(OnClickCallback));OneSecondHandler += CallbackSample_OneSecondHandler;}private void CallbackSample_OneSecondHandler(object sender, EventArgs e){Debug.Log("1S 事件被触发");}private async UniTaskVoid OnClickCallback(){float time = Time.time;UniTaskCompletionSource source = new UniTaskCompletionSource();FallTarget(Target.transform, source).Forget();await source.Task;// UniTaskCompletionSource产生的UnitTask是可以复用的Debug.Log($"耗时 {Time.time - time}秒");}/// <summary>/// UniTask运行中执行回调/// UniTaskCompletionSource是对UniTask和CancellationToken的封装/// </summary>private async UniTask FallTarget(Transform targetTrans, UniTaskCompletionSource source){Vector3 startPosition = targetTrans.position;float fallTime = 2f;float elapsedTime = 0;while (elapsedTime <= fallTime){elapsedTime += Time.deltaTime;//当下落时间超过1秒时设置操作if (elapsedTime > 1f){OneSecondHandler?.Invoke(this,EventArgs.Empty);// 表示操作完成source.TrySetResult();// 失败// source.TrySetException(new SystemException());// 取消// source.TrySetCanceled(someToken);// 泛型类UniTaskCompletionSource<T> SetResult是T类型,返回UniTask<T>}float fallY = 0.5f * G * elapsedTime * elapsedTime;targetTrans.position = startPosition + Vector3.down * fallY;await UniTask.Yield(this.GetCancellationTokenOnDestroy());}}private void OnDestroy(){OneSecondHandler -= CallbackSample_OneSecondHandler;}
}
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;public class AsyncReactivePropertySample : MonoBehaviour
{public int maxHp = 100;public float totalChangeTime = 1f;public Text ShowHpText;public Text StateText;public Text ChangeText;public Slider HpSlider;public Image HpBarImage;public Button HealButton;public Button HurtButton;private AsyncReactiveProperty<string> currentHpStr;private AsyncReactiveProperty<int> currentHp;private int maxHeal = 10;private int maxHurt = 10;private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();private CancellationTokenSource _linkedTokenSource;private void Start(){// 设置AsyncReactivePropertycurrentHpStr = new AsyncReactiveProperty<string>($"当前血量:{maxHp}/{maxHp}");currentHp = new AsyncReactiveProperty<int>(maxHp);HpSlider.maxValue = maxHp;HpSlider.value = maxHp;currentHp.Subscribe(OnHpChange);CheckHpChange(currentHp).Forget();CheckFirstLowHp(currentHp).Forget();currentHpStr.BindTo(ShowHpText);HealButton.onClick.AddListener(OnClickHeal);HurtButton.onClick.AddListener(OnClickHurt);_linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token,this.GetCancellationTokenOnDestroy());}private void OnClickHeal(){ChangeHp(Random.Range(0, maxHeal));}private void OnClickHurt(){ChangeHp(-Random.Range(0, maxHurt));}private void ChangeHp(int deltaHp){currentHp.Value = Mathf.Clamp(currentHp.Value + deltaHp, 0, maxHp);currentHpStr.Value = $"当前血量:{currentHp.Value}/{maxHp}";}/// <summary>/// currentHp变化时修改提示信息/// </summary>private async UniTaskVoid CheckHpChange(AsyncReactiveProperty<int> hp){int hpValue = hp.Value;// WithoutCurrent 忽略初始值await hp.WithoutCurrent().ForEachAsync((_, index) =>{ChangeText.text = $"血量发生变化 第{index}次 变化{hp.Value - hpValue}";hpValue = hp.Value;}, this.GetCancellationTokenOnDestroy());}/// <summary>/// currentHp低于临界值,显示提示信息/// </summary>private async UniTaskVoid CheckFirstLowHp(AsyncReactiveProperty<int> hp){await hp.FirstAsync((value) => value < maxHp * 0.4f, this.GetCancellationTokenOnDestroy());StateText.text = "首次血量低于界限,请注意!";}private async UniTaskVoid OnHpChange(int hp){_cancellationTokenSource.Cancel();_cancellationTokenSource = new CancellationTokenSource();_linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token,this.GetCancellationTokenOnDestroy());await SyncSlider(hp, _linkedTokenSource.Token);}/// <summary>/// 同步血条/// </summary>private async UniTask SyncSlider(int hp, CancellationToken token){var sliderValue = HpSlider.value;float needTime = Mathf.Abs((sliderValue - hp) / maxHp * totalChangeTime);float useTime = 0;while (useTime < needTime){useTime += Time.deltaTime;bool result = await UniTask.Yield(PlayerLoopTiming.Update, token).SuppressCancellationThrow();if (result){return;}var newValue = (sliderValue + (hp - sliderValue) * (useTime / needTime));SetNewValue(newValue);}}private void SetNewValue(float newValue){if (!HpSlider) return;HpSlider.value = newValue;HpBarImage.color = HpSlider.value / maxHp < 0.4f ? Color.red : Color.white;}
}