- 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以上に便利で習得必須になりそうなので、リリースされるのを楽しみにしています!!