UniTask v2のRCが出たので触ってみた (RC4対応版)
- UniTask v2の全機能を紹介してるわけではありません。新機能一覧はReleaseページを追ってください。
- RCなので、正式版ではインターフェイスなどもろもろ代わっている可能性があります。
- UniTaskのReleaseページから最新版を落として使いましょう。
雑談
Unityでもasync/awaitが当たり前のように使われるようになったこの2020年、
コルーチンはもちろん、Taskが無かったからRxで補っていたところも、
全部UniTaskで置き換えればよくね・・・?と思っているわけです。
そんなところに、UniTask v2の話が流れてきたので触ってみました。
UniTask v2、rc1出しました。このまま行くとそろそろリリース……!そして新機能的にはAsyncReactivePropertyを追加しました。 https://t.co/HPV4a550FE
— neuecc (@neuecc) May 16, 2020
LINQでawait出来る
var hoge = new[] { 1, 2, 3 };
var fuga = await hoge.Select(async x =>
{
await UniTask.Delay(1000);
return x + 1;
});一番待ち望んでいた!!というか、一番使いみちが分かりやすいのがこれ!
Selectで待てる、もちろんMaxでもなんでも待てる。
今まで、ああ〜ここLINQで書いてたのに非同期混ざったからforeachで展開しよ〜ってやってたのがそのまま通るようになって嬉しい!
UniTask.Createが追加
UniTask.Create(async () =>
{
await UniTask.Delay(1000);
Debug.Log("1秒経過");
}).Forget();(なんで無かったんだろう)
ForEachAwaitAsync
var hoge = new[] { 1, 2, 3 };
await hoge.ToUniTaskAsyncEnumerable().Select(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(1));
return x + 1;
}).ForEachAwaitAsync(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(1));
Debug.Log($"Output {x}");
});配列を、時間のかかる処理で回しながら、時間のかかる表示を行う、みたいなことが簡単に出来る。
UniTaskAsyncEnumerable.EveryUpdate
UniTaskAsyncEnumerable.EveryUpdate().ForEachAsync(x =>
{
Debug.Log("Update");
}).Forget();毎フレーム飛んでくるので、これは毎フレームUpdateと表示される。
UniTaskAsyncEnumerable.EveryUpdate().ForEachAwaitAsync(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(1));
Debug.Log("Update");
}).Forget();ForEachAwaitAsyncの中のTaskを処理している最中は次の値が来ることが無い。
つまり、このコードでは1秒に1回"Update"と表示される。
何が嬉しいかは次の OnClickAsAsyncEnumerable で。
OnClickAsAsyncEnumerable
UnityEngine.UI.Button button;
button.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x =>
{
Debug.Log("ボタン押した処理開始!");
await UniTask.Delay(TimeSpan.FromSeconds(1));
Debug.Log("ボタン押した処理終了!");
});こうしておくと、ボタンをどれだけ連打しても処理が多重に走ることが無い。嬉しい。
AsyncReactiveProperty (RC2挙動変更)
var hp = new AsyncReactiveProperty<int>(10);
Debug.Log("Set hp = 10");
hp.ForEachAwaitAsync(async x =>
{
Debug.Log($"Start hp = {x}");
await UniTask.Delay(TimeSpan.FromSeconds(1));
Debug.Log($"Finish hp = {x}");
}).Forget();
await UniTask.Delay(TimeSpan.FromSeconds(0.3));
Debug.Log("Set hp = 20");
hp.Value = 20;
await UniTask.Delay(TimeSpan.FromSeconds(0.8));
Debug.Log("Set hp = 30");
hp.Value = 30;
// Output
/*
0.0 | Set hp = 10
0.0 | Start hp = 10
0.3 | Set hp = 20
1.0 | Finish hp = 10
1.1 | Set hp = 30
1.1 | Start hp = 30
2.1 | Finish hp = 30
*/ReactivePropertyから想像されるように、現在の値がまず流れてくる。
これも、ForEachAwaitAsyncで処理中の時に流れてきた値は流れてこないので注意。
(このサンプルでは20の値が処理されない。)
Queue (RC2追加)
ForEachAwaitAsyncで処理を回してるんだけど、
全ての値を処理したい!というケースはQueueを挟みましょう。
var hp = new AsyncReactiveProperty<int>(10);
Debug.Log("Set hp = 10");
// ↓ さっきのコードのここにQueue挟んだだけ
hp.Queue().ForEachAwaitAsync(async x =>
{
Debug.Log($"Start hp = {x}");
await UniTask.Delay(TimeSpan.FromSeconds(1));
Debug.Log($"Finish hp = {x}");
}).Forget();
await UniTask.Delay(TimeSpan.FromSeconds(0.3));
Debug.Log("Set hp = 20");
hp.Value = 20;
await UniTask.Delay(TimeSpan.FromSeconds(0.8));
Debug.Log("Set hp = 30");
hp.Value = 30;
// Output
/*
0.0 | Set hp = 10
0.0 | Start hp = 10
0.3 | Set hp = 20
1.0 | Finish hp = 10
1.0 | Start hp = 20
1.1 | Set hp = 30
2.0 | Finish hp = 20
2.0 | Start hp = 30
3.0 | Finish hp = 30
*/ForEachAwaitAsync するつもりで ForEachAsync しないように! (RC4で対応済)
await hoge.ToUniTaskAsyncEnumerable().ForEachAwaitAsync(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(1));
Debug.Log($"Output {x}");
});このコードは、1秒後から、hogeの内容が1秒毎に出てくる。
間違えて ForEachAsync にしちゃった!
await hoge.ToUniTaskAsyncEnumerable().ForEachAsync(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(1));
Debug.Log($"Output {x}");
});これでもビルドは通ってしまう。悲しい。
RC4でビルドエラーになるようになりました。
うっかりActionにTask渡しちゃうやつってビルドエラーに出来たのか・・・勉強になります。
まとめ
今のところUniRx, UniTask(v1)でコードを書いていますが
UniTask v2はv1以上に便利で習得必須になりそうなので、リリースされるのを楽しみにしています!!