UniTaskとは
非同期処理とは
Unitask準備
- 以下のGit URLをパッケージマネジャーに入力する
https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
- スクリプトの先頭にusing Cysharp.Threading.Tasksを追加する
Unitaskで汎用的に使える機能
Unitaskにはいくつか機能がありますので、使用頻度の高そうな部分をこちらに載せていきます。
await Unitask.Delay
一定時間を待って演出を入れたり、遅延実行をするときによく使用する。
// UniTask.Delay
private async UniTask sample1()
{
message = "3";
await UniTask.Delay(TimeSpan.FromSeconds(1));
//await UniTask.Delay(UniTask.Delay(1000));
message = "2";
await UniTask.Delay(TimeSpan.FromSeconds(1));
message = "1";
await UniTask.Delay(TimeSpan.FromSeconds(1));
}
UniTask.DelayFrameというフレーム単位でも指定はできるよ!
await UniTask.Yield
処理をちょうど1フレームだけ遅らせたい場合に使用する。
UniTask.Delayよりも軽量で、例えばStart()の処理内で初期化した後に、1フレーム後にUIを表示する場面などに有効。
// UniTask.Yield
private async UniTask sample2()
{
message = "1";
await UniTask.Yield();
message = "2";
await UniTask.Yield();
message = "3";
await UniTask.Yield();
}
await SceneManager.LoadSceneAsync(“シーン名”).ToUniTask()
ToUnitask()を付けて、awaitすることでシーンの読み込み終了後にフェードインやフェードアウトなどの追加の処理を実行できる。
通常のLoadSceneAsyncでは読み込み完了と同時にシーンが切り替わるため、演出が挟めないので演出を入れる場合に有効。
// SceneManager.LoadSceneAsync
private async UniTask sample3()
{
message = "シーン遷移スタート";
await SceneManager.LoadSceneAsync("UnitaskTest2").ToUniTask();
message = "シーン遷移完了";
}
UniTask.WhenAll(処理1,処理2,…)
複数タスクを同時に並列実行して、全て完了するまで待機をする。引数は最大16個。
タスクは1つずつ実行ではなく、並列で実行をして待機をする。
例として、ロード画面でUIアニメーションとデータ取得を並列に実行して、両方終わったら次に進むなどで使える。
// UniTask.WhenAll
private async UniTask sample3()
{
await UniTask.WhenAll(task1(), task2());
message = "Finish";
}
private async UniTask task1()
{
message = "task1";
await UniTask.Delay(TimeSpan.FromSeconds(2));
}
private async UniTask task2()
{
message = "task2";
await UniTask.Delay(TimeSpan.FromSeconds(2));
}
UniTask.WhenAny(処理1,処理2,…)
先に完了したタスクが返るまで待機をする。
例として、ゲームで制限時間以内にボタンが押されたかどうか判定をする。httpリクエストを複数実行して、先に処理ができた方を実行する。複数ボタンがある中でどれか押されるまで待機するなど。
// UniTask.WhenAny
private async UniTask sample5()
{
message = "待機中";
// 時間経過タスク(5秒)
var timeoutTask = UniTask.Delay(TimeSpan.FromSeconds(5));
// キーボード入力タスク(スペースキー)
var keyInputTask = WaitForSpaceKey();
// どちらが先に完了するかを判定
var result = await UniTask.WhenAny(timeoutTask, keyInputTask);
if (result == 0)
{
message = "アウト";
}
else
{
message = "ゲームクリア";
}
// 結果を少し表示してからリセット
await UniTask.Delay(TimeSpan.FromSeconds(2));
message = "";
}
private async UniTask WaitForSpaceKey()
{
while (true)
{
if (Input.GetKeyDown(KeyCode.Space))
{
break;
}
await UniTask.Yield();
}
}
CancellationToken
シーン遷移やゲームオブジェクトの破棄の際にタスクを安全に中断させることができる。
例として、10秒後にヒントを表示するタスクがあって、プレイヤーが操作をしたら即キャンセルをするといったことができる。
// CancellationToken
private async UniTask sample6()
{
CancellationToken token = this.GetCancellationTokenOnDestroy();
try
{
await UniTask.Delay(TimeSpan.FromSeconds(10), cancellationToken: token);
message = "10秒経過しました";
}
catch (OperationCanceledException)
{
message = "キャンセルされました";
}
}
async UniTask<T>
コルーチンでは戻り値を返せないが、UniTask<T>であれば、非同期完了時に値を返却ができる。
例として、ボタンの選択結果やスコア計算後の結果を返却する際に使用ができる。
// UniTask
private async UniTask sample7()
{
await UniTask.Delay(TimeSpan.FromSeconds(2));
return "Hello";
}
async UniTaskVoid
UnitaskVoidは戻り値も例外通知も行わない特殊なもの。
以下のようにUnity標準のイベント処理ではUnitaskは使用できないので、Start()などのイベント処理で非同期処理を呼び出したいときに限定的に使用をする。
// UniTaskVoid
private async UniTaskVoid Start()
{
await UniTask.Delay(TimeSpan.FromSeconds(2));
message = "ゲームスタート!";
}
//これは実行できない
private async UniTask Start()
{
await UniTask.Delay(1000);
Debug.Log("スタート!");
}
まとめ
標準のコルーチンよりもコードの可読性が向上するので、私も積極的に使おうと思います。Unitaskにはこれ以外にも機能があるので場面に合わせて使い分けれるといいかもしれません。