工程文件下载
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;
//百度一张图片:右击,在新标签页中打开图像,复制url
private 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包装了OnClickTest
TestButton.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()
{
//不需要等待时候就调用Forget
FallTarget(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()
{
// 设置AsyncReactiveProperty
currentHpStr = 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;
}
}