Unity插件 Unitask学习日志
Debug.Log("Untity需求");
UniTask 功能依赖于 C# 7.0( task-like custom async method builder feature ) 所以需要的 Unity 最低版本是Unity 2018.3 ,
官方支持的最低版本是Unity 2018.4.13f1.
下载地址
https://github.com/Cysharp/UniTask
点击这里可以查阅中文文档
在Unity 2020,2021 中使用UPM下载会找不到,可以使用2022版本的unity可以在upm中找到。
安装方式:
下载zip之后解压,
复制Plugins
到Unity项目中即可。
Content
-
使用Unitask 的最低版本是:Unity 2018.4.13f1.
_ = DelayFrame_Test(100);
中的_=
在 丢弃 符号,不是没有意义的。用于忽略返回值的语法,通常用于那些不需要处理返回值的方法调用,但仍然希望调用该方法。 -
仅仅记录了github中文档的一小部分,文档的后面有“错误处理”,“超时处理”,“进度”,“线程切换” 等。详情请查阅github文档
-
写法是:
private async UniTask 方法名称
{
await 操作
}
private async UniTask<T> 方法名称
{
await 操作
retun 返回值
}
警告
Debug.Log("警告");
永远不应在 ValueTask 实例上执行以下操作:
多次await实例。
多次调用 AsTask。
在操作尚未完成时调用.Result 或 .GetAwaiter().GetResult(),多次调用也是不允许的。
混用上述行为更是不被允许的。
如果您执行上述任何操作,则结果是未定义。
_ = MAKEERROR();
var task = UniTask.DelayFrame(10);
await task;
await task; // 寄了, 抛出异常
如果物体被隐藏, UniTask.WaitUntil(() => stateLock == true) 也会被执行
using Cysharp.Threading.Tasks;
using System;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
adadasd dasda;
private void Awake()
{
GameObject game = new GameObject();
dasda = game.AddComponent<adadasd>();
_ = twaii();
gameObject.SetActive(false);
Debug.Log("1时间:" + Time.frameCount +
",dasda.stateLock 状态是;" + dasda.stateLock +
",NewBehaviourScript .active:" + gameObject.activeSelf);
}
private async UniTask twaii()
{
Debug.Log("2时间:" + Time.frameCount +
",dasda.stateLock 状态是;" + dasda.stateLock +
",NewBehaviourScript .active:" + gameObject.activeSelf);
await UniTask.WaitUntil(() => dasda.stateLock == true);
Debug.Log("3时间:" + Time.frameCount +
",dasda.stateLock 状态是;" + dasda.stateLock +
",NewBehaviourScript .active:" + gameObject.activeSelf);
}
}
using UnityEngine;
public class adadasd : MonoBehaviour
{
public bool stateLock = false;
private void Start()
{
Invoke(nameof(ChanegeState), 2.0f);
}
private void ChanegeState()
{
stateLock = true;
}
}
使用异步发送http请求
将start 换成
private async void Start()
{
Debug.Log("异步的start方法");
_ = DoSomeThing();
_ = GetTextAsync();
Debug.Log("start End");
}
也是可以执行start方法的。
using Best.HTTP;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
public class SendRequestByUnitask : MonoBehaviour
{
private void Start()
{
Debug.Log("异步的start方法");
_ = DoSomeThing();//使用webrequst发送请求
_ = GetTextAsync();//使用besthttp发送请求
Debug.Log("start End");
}
//在这里处理请求的连接的东西,
private async UniTask<string> DoSomeThing()
{
UnityWebRequest tmp_webRequest = UnityWebRequest.Get("https://www.baidu.com");
string tmp_data = await GetTextAsync(tmp_webRequest);
Debug.Log(tmp_data);
return tmp_data;
}
//只有发送,然后等待返回数据即可。
async UniTask<string> GetTextAsync(UnityWebRequest _req)
{
var op = await _req.SendWebRequest();
Debug.Log(op.downloadHandler.text);
return op.downloadHandler.text;
}
//处理请求的http链接,,
async UniTask<string> GetTextAsync()
{
HTTPRequest _request = new HTTPRequest("https://www.baidu.com", HTTPMethods.Get);
var tmp_Data = await BestHttpSendRequest(_request);
return tmp_Data;
}
//只有发送,并等待返回到数据
async UniTask<string> BestHttpSendRequest(HTTPRequest _request)
{
await _request.Send();
await UniTask.WaitUntil(() => _request.Response.Data != null);
return _request.Response.DataAsText;
}
}
全部代码记录
void Start()
{
Debug.Log("进入start方法");
//UniTask<string> ssst = CreateCubeAndReadText();
_ = DelayFrame_Test(100);
//_ = SendGetHttpRequest();
//_ = WaitUnityPlayerLoop();
//_ = ReplaceYield();
//_ = WaitForEndOfFrame();
//_ = WaitUntilCondition();
//_ = WaitUntilValueChangedCondition();
//_ = FooCoroutineEnumerator();
//_ = TaskRunTest();
//_ = await2await();
//_ = WaitLoadSceneAsync();
//_ = LoadManyAsync();
//_ = TwoTimes();
//_ = FooAsync();
Debug.Log("start方法结束");
}
/// <summary>
/// 异步创建方块,和读取文件
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
private async UniTask<string> CreateCubeAndReadText()
{
Debug.Log("Unitask进入 异步");
var asset = await Resources.LoadAsync<TextAsset>("fulman");
for (int i = 0; i < 2; i++)
{
GameObject.CreatePrimitive(PrimitiveType.Capsule);
}
Debug.Log("Unitask异步读取文件,创建物体结束");
return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found");
}
/// <summary>
/// 看不懂
/// </summary>
/// <returns></returns>
private async UniTask _GetCancellationTokenOnDestroy()
{
// llation 会启用取消功能,GetCancellationTokenOnDestroy 表示获取一个依赖对象生命周期的Cancel句柄,当对象被销毁时,将会调用这个Cancel句柄,从而实现取消的功能
//var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
}
/// <summary>
/// 等待100帧
/// </summary>
/// <returns></returns>
private async UniTask DelayFrame_Test(int frameCount)
{
Debug.Log("等待100帧开始" + Time.frameCount);
// 等待一个基于帧的延时操作(就像一个协程一样)
await UniTask.DelayFrame(frameCount);
Debug.Log("等待100帧结束" + Time.frameCount);
}
/// <summary>
/// 异步发送http的get请求
/// </summary>
/// <returns></returns>
private async UniTask<string> SendGetHttpRequest()
{
Debug.Log("异步发送http的get请求开始");
string webText =
(await UnityWebRequest.Get
("https://github.com/Cysharp/UniTask/blob/master/README_CN.md")
.SendWebRequest()).downloadHandler.text;
Debug.Log("异步发送http的get请求结束" + webText);
return webText;
}
/// <summary>
/// 等待一个Unity的生命周期
/// </summary>
/// <returns></returns>
private async UniTask WaitUnityPlayerLoop()
{
Debug.Log("异步开始等待Unity的一个生命周期,LastInitialization");
await UniTask.Yield(PlayerLoopTiming.LastInitialization);
Debug.Log("异步结束等待Unity的一个生命周期,LastInitialization");
var go = GameObject.CreatePrimitive(PrimitiveType.Capsule);
go.transform.position = new Vector3(3, 0, 3);
}
/// <summary>
/// Yield的替代,
/// </summary>
/// <returns></returns>
private async UniTask ReplaceYield()
{
Debug.Log("UniTask.Yield当前帧数是start:" + Time.frameCount);
//等下一帧执行
await UniTask.Yield();
Debug.Log("UniTask.Yield之后的帧数是end:" + Time.frameCount);
Debug.Log("第二个开始");
Debug.Log("NextFrame当前帧数是start:" + Time.frameCount);
//等下一帧执行
await UniTask.NextFrame();
Debug.Log("NextFrame等待一帧之后end:" + Time.frameCount);
}
/// <summary>
/// 等待这帧结束后进行操作
/// </summary>
/// <returns></returns>
private async UniTask WaitForEndOfFrame()
{
Debug.Log("帧开始:" + Time.frameCount);
await UniTask.WaitForEndOfFrame();
Debug.Log("帧结束 :" + Time.frameCount);
}
/// <summary>
/// 某个条件满足的时候触发
/// </summary>
/// <returns></returns>
private async UniTask WaitUntilCondition()
{
GameObject goEnmty = new GameObject("goEnmty");
Debug.Log("方块还在转圈");
// yield return WaitUntil 替代方案
//等待满足于某个条件的时候执行
await UniTask.WaitUntil(() => goEnmty.activeSelf == false);
}
/// <summary>
/// 当某个值改变的时候触发
/// </summary>
/// <returns></returns>
private async UniTask WaitUntilValueChangedCondition()
{
Debug.Log("监听gameob 是否改变位置");
GameObject gameob = new GameObject("gameob");
//开始监听goRotateCube 的X是否 改变位置
await UniTask.WaitUntilValueChanged(gameob, gorotatecube => gameob.transform.localPosition.x);
Debug.Log(" 监听到gameob 位置改变");
}
/// <summary>
/// 可以等待一个协程
/// </summary>
/// <returns></returns>
private async UniTask FooCoroutineEnumerator()
{
await SayHello100Times();
Debug.Log("100次hello man说完啦!");
}
/// <summary>
/// 说100次你好
/// </summary>
/// <returns></returns>
private IEnumerator SayHello100Times()
{
yield return new WaitForSeconds(2);
for (int i = 0; i < 100; i++)
{
Debug.Log("hello man");
}
}
GameObject rotateCube;
/// <summary>
/// 可以等待一个task run
/// </summary>
private async UniTask TaskRunTest()
{
rotateCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
int resule = await Task.Run(StartDotweenAnimation);
rotateCube.gameObject.name = "rotateCube";
}
/// <summary>
/// 启动dotween动画
/// </summary>
/// <returns></returns>
private int StartDotweenAnimation()
{
rotateCube.gameObject.transform.DORotate(new Vector3(180, 0, 0), 2.0f).SetLoops(-1, LoopType.Yoyo);
return 0;
}
/// <summary>
/// 一个方法里两个await
/// </summary>
private async UniTask await2await()
{
Debug.Log("await2await:" + DateTime.Now);
await UniTask.WaitForSeconds(2);
Debug.Log("await2await:" + DateTime.Now);
await UniTask.WaitForSeconds(2);
Debug.Log("await2await:" + DateTime.Now);
}
private async UniTask WaitLoadSceneAsync()
{
Debug.Log("等待场景切换");
Debug.Log("没有场景");
//await SceneManager.LoadSceneAsync("scene2");
Debug.Log("场景切换完毕");
}
/// <summary>
/// 当销毁对象的的时候取消这个对象身上的等待
/// </summary>
/// <returns></returns>
private async UniTask CancelawaitWhenDestroyObj()
{
// .WithCancellation 会启用取消功能,GetCancellationTokenOnDestroy 表示获取一个依赖对象生命周期的Cancel句柄,当对象被销毁时,将会调用这个Cancel句柄,从而实现取消的功能
//var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());
}
/// <summary>
/// 等待一个协程
/// </summary>
/// <returns></returns>
private async UniTask awaitAIEnumerator()
{
Debug.Log("开始等待携程");
await beAwaitIEnumerator();
}
/// <summary>
/// 被等待的携程
/// </summary>
/// <returns></returns>
private IEnumerator beAwaitIEnumerator()
{
yield return new WaitForSeconds(1);
Debug.Log("携程等待完毕");
}
/// <summary>
/// 并行加载
/// </summary>
/// <returns></returns>
public async UniTaskVoid LoadManyAsync()
{
GameObject tmp_gocanvas = new GameObject("tmp_canvas");
GameObject tmp_goimage1 = new GameObject("tmp_goimage1");
GameObject tmp_goimage2 = new GameObject("tmp_goimage2");
GameObject tmp_goimage3 = new GameObject("tmp_image3");
var tmp_canvas = tmp_gocanvas.AddComponent<Canvas>();
tmp_canvas.renderMode = RenderMode.ScreenSpaceOverlay;
var tmp_image1 = tmp_goimage1.AddComponent<Image>();
var tmp_image2 = tmp_goimage2.AddComponent<Image>();
var tmp_image3 = tmp_goimage3.AddComponent<Image>();
tmp_goimage1.transform.parent = tmp_gocanvas.transform;
tmp_goimage2.transform.parent = tmp_gocanvas.transform;
tmp_goimage3.transform.parent = tmp_gocanvas.transform;
tmp_goimage1.transform.localPosition = new Vector3(0, 0, 0);
tmp_goimage2.transform.localPosition = new Vector3(100, 0, 0);
tmp_goimage3.transform.localPosition = new Vector3(200, 0, 0);
Debug.Log("并行加载");
var (a, b, c) = await UniTask.WhenAll(
LoadAsSprite("blenderIcon"),
LoadAsSprite("blenderIcon"),
LoadAsSprite("blenderIcon"));
tmp_image1.sprite = a;
tmp_image2.sprite = b;
tmp_image3.sprite = c;
}
async UniTask<Sprite> LoadAsSprite(string path)
{
var resource = await Resources.LoadAsync<Sprite>(path);
return (resource as Sprite);
}
/// <summary>
/// 会发生错误,错误的写法!
/// </summary>
/// <returns></returns>
private async UniTask MAKEERROR()
{
var task = UniTask.DelayFrame(10);
await task;
await task; // 寄了, 抛出异常
}
/// <summary>
/// 一个TASK await两次
/// </summary>
/// <returns></returns>
private async UniTask TwoTimes()
{
Debug.Log("等待一次" + DateTime.Now);//等待一次2024/8/22 23:21:21
var task = UniTask.WaitForSeconds(2).Preserve();
await task;
Debug.Log("等待二次" + DateTime.Now);//等待二次2024/8/22 23:21:24
await task;
Debug.Log("结束" + DateTime.Now);//结束2024/8/22 23:21:24
}
#region Unitask超时处理
TimeoutController timeoutController = new TimeoutController(); // 复用timeoutController
async UniTask FooAsync()
{
Debug.Log("超时处理,看不懂");
try
{
// 你可以通过 timeoutController.Timeout(TimeSpan) 传递到 cancellationToken.
await UnityWebRequest.Get("http://foo").SendWebRequest()
.WithCancellation(timeoutController.Timeout(TimeSpan.FromSeconds(5)));
timeoutController.Reset(); // 当await完成后调用Reset(停止超时计时器,并准备下一次复用)
}
catch (OperationCanceledException ex)
{
if (timeoutController.IsTimeout())
{
UnityEngine.Debug.Log("timeout");
}
}
}
#endregion
#region async void 与 async UniTaskVoid 对比
/*
async void是一个原生的 C# 任务系统,因此它不能在 UniTask 系统上运行。
也最好不要使用它。async UniTaskVoid是async UniTask的轻量级版本,
因为它没有等待完成并立即向UniTaskScheduler.UnobservedTaskException报告错误.
如果您不需要等待(即发即弃),那么使用UniTaskVoid会更好。不幸的是,要解除警告,您需要在尾部添加Forget().
*/
public async UniTaskVoid FireAndForgetMethod()
{
// do anything...
await UniTask.Yield();
}
public void Caller1()
{
FireAndForgetMethod().Forget();
}
/*
UniTask 也有Forget方法,类似UniTaskVoid且效果相同。但是如果你完全不需要使用await,UniTaskVoid会更高效。
*/
public async UniTask DoAsync()
{
// do anything...
await UniTask.Yield();
}
public void Caller2()
{
DoAsync().Forget();
}
/*
要使用注册到事件的异步 lambda,请不要使用async void. 相反,您可以使用UniTask.Action 或 UniTask.UnityAction,两者都通过async UniTaskVoid lambda 创建委托。
*/
Action actEvent;
UnityAction unityEvent; // UGUI特供
private void AddDelegate()
{
// 这样是不好的: async void
Debug.Log("这里注释了,不安全代码");
//actEvent += async () => { };
//unityEvent += async () => { };
// 这样是可以的: 通过lamada创建Action
actEvent += UniTask.Action(async () => { await UniTask.Yield(); });
unityEvent += UniTask.UnityAction(async () => { await UniTask.Yield(); });
}
#endregion
#region UniTaskTracker
//对于检查(泄露的)UniTasks 很有用。您可以在Window -> UniTask Tracker中打开跟踪器窗口。
/*
Enable AutoReload(Toggle) - 自动重新加载。
Reload - 重新加载视图(重新扫描内存中UniTask实例,并刷新界面)。
GC.Collect - 调用 GC.Collect。
Enable Tracking(Toggle) - 开始跟踪异步/等待 UniTask。性能影响:低。
Enable StackTrace(Toggle) - 在任务启动时捕获 StackTrace。性能影响:高。
UniTaskTracker 仅用于调试用途,因为启用跟踪和捕获堆栈跟踪很有用,但会对性能产生重大影响。
推荐的用法是启用跟踪和堆栈跟踪以查找任务泄漏并在完成时禁用它们。
*/
#endregion