Unity/UniTask
目次
- 1 ダウンロード
- 2 ScriptableSingletonエラーが出る時
- 3 Taskとの比較
- 4 サンプル
- 5 IEnumeratorが戻り値のものを実行
- 6 StartCoroutine処理内のIEnumeratorで、UniTaskを使う
- 7 UniTask内で、CoroutineのIEnumeratorを使う
- 8 マルチスレッド
- 9 ボタンを押して処理
- 10 条件を満たしたら先へ
- 11 入力待機
- 12 yield return nullと同じ機能をUniTaskで
- 13 数秒待つ
- 14 呼び出し元も待つように
- 15 関数を2つ以上挟む
- 16 並列処理
- 17 インターフェイスの書き方
- 18 UniTaskVoidという型
- 19 参考
ダウンロード
PackageManagerでインストール
- Unityメインメニュー/Window/PackageManager/右上の+
- Add package from git URL
https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
unitypackageでインストール
https://github.com/Cysharp/UniTask/releases
UniTask.2.2.5.unitypackageをDLしてインストール
ScriptableSingletonエラーが出る時
ScriptableSingleton already exists. Did you query the singleton in a constructor? UnityEditor.PackageManager.UI.PackageManagerProjectSettings:.ctor () (at /Users/bokken/buildslave/unity/build/Modules/PackageManagerUI/Editor/Services/ProjectSettings/PackageManagerProjectSettings.cs:102)
がでるときは、Pluginsに直接、csをインストールしてないか確認、unitypackageからインストールしてみる。
Taskとの比較
UniTaskは、Taskより軽く、SynchronizationContextへの依存がない。
Taskサンプルはこちら
Unity/Csharp/別スレッド [ショートカット]
サンプル
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks; // Taskサンプル用
// using UniRx.Async; // UniTask ver1
using Cysharp.Threading.Tasks; // UniTask ver2
public class SampleScene : MonoBehaviour
{
void Start()
{
Exec();
}
async void Exec()
{
string result = await ExecUniTask();
Debug.Log("Exec " + result); // Exec Hello!!
}
async UniTask<string> ExecUniTask()
{
return await Task.Run(() => "Hello!!");
}
}
IEnumeratorが戻り値のものを実行
UniTaskだと、IEnumeratorのものを、StartCoroutineではなくてawaitで実行できる。
using Cysharp.Threading.Tasks;
public class SampleScene : MonoBehaviour
{
void Start()
{
ExecInvoke();
}
async void ExecInvoke()
{
await DelayMethod1(5.0f, 123);
}
IEnumerator DelayMethod1(float delay, int hoge)
{
yield return new WaitForSeconds(delay);
// ここに処理を追加
Debug.Log("DelayMethod1 exec " + hoge);
}
}
StartCoroutine処理内のIEnumeratorで、UniTaskを使う
.ToCoroutine()を使えば良い。
using Cysharp.Threading.Tasks;
public class CoroutineAsyncScene : MonoBehaviour
{
IEnumerator Start()
{
Debug.Log("start");
yield return WaitAsync().ToCoroutine();
yield return StartCoroutine(WaitFor());
Debug.Log("end");
}
IEnumerator WaitFor()
{
yield return new WaitForSeconds(1f);//1秒待つ
Debug.Log("WaitFor 1s!!");
}
async UniTask WaitAsync()
{
await UniTask.Delay(1000);//1秒待つ
Debug.Log("WaitAsync 1s");
}
}
UniTask内で、CoroutineのIEnumeratorを使う
using System.Collections;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class UniTaskToCoroutineScene : MonoBehaviour
{
async UniTask Start()
{
Debug.Log("start");
// CoroutineをIEnumeratorとして直接UniTaskに変換する
await WaitFor().ToUniTask();
// WaitAsyncを直接呼び出す
await WaitAsync();
Debug.Log("end");
}
IEnumerator WaitFor()
{
yield return new WaitForSeconds(1f); // 1秒待つ
Debug.Log("WaitFor 1s!!");
}
async UniTask WaitAsync()
{
await UniTask.Delay(1000); // 1秒待つ
Debug.Log("WaitAsync 1s");
}
}
マルチスレッド
public class MultiThreadScene : MonoBehaviour
{
async void Start()
{
await ExecAsync();
}
async UniTask ExecAsync()
{
await UniTask.SwitchToThreadPool(); // 別スレッドへ
// ここに処理記述
Debug.Log(Thread.CurrentThread.ManagedThreadId);
await UniTask.SwitchToMainThread(); // メインスレッドに戻す
}
}
参考:https://light11.hatenadiary.com/entry/2021/05/27/203956
ボタンを押して処理
void Start()
{
cacheButton.onClick.AddListener(async () => { await ExecDel(); });
}
async void ExecDel()
{
}
条件を満たしたら先へ
async void Exec()
{
await UniTask.WaitUntil(() => transform.position.y < 0);
Debug.Log("ok");
}
MainCameraにaddComponentしてたら、Yの値を-1にすると先へ進む
入力待機
using UnityEngine;
using Cysharp.Threading.Tasks;
public class WaitScene : MonoBehaviour
{
void Start()
{
WaitInput();
}
async void WaitInput()
{
await UniTask.WaitUntil(() => Input.GetKeyDown(KeyCode.Return));
// await UniTask.WaitWhile(() => !Input.GetKeyDown(KeyCode.Return)); // 条件に当てはまらないときは、先にすすめる
// ここに処理を追加
Debug.Log("return!!");
}
}
yield return nullと同じ機能をUniTaskで
GameObjectを削除する機能で使用する例
using Cysharp.Threading.Tasks;
using UnityEngine;
public class Sample : MonoBehaviour
{
public GameObject target;
async UniTaskVoid DestroyAndWait()
{
Destroy(target);
// 次のフレームまで待機(yield return null と同じ)
await UniTask.Yield();
Debug.Log("確実に Destroy 済み");
}
}
数秒待つ
async void Hoge() {
await UniTask.Delay(1000); // ms
}
Invokeとかと違って、シーンが変更されても実行されるので気をつける。
呼び出し元も待つように
UniTaskを戻り値に指定して、awaitで呼び出すと待つようになる。 逆にvoidを戻り値にして、awaitなしで呼び出すと、待たずに処理する。
void Start()
{
Main();
}
async void Main()
{
Debug.Log("start");
await HogeWait();
Debug.Log("middle"); // 0.1秒後に表示される
Hoge();
Debug.Log("end"); // middleのすぐ後に表示される
}
async UniTask HogeWait()
{
await UniTask.Delay(1000); // ms
}
async void Hoge()
{
UniTask.Delay(1000); // ms
}
UniTask.Delayのキャンセル
using Cysharp.Threading.Tasks;
using System.Threading;
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;
ViewCntCancelAsync(cancellationToken);
cancellationTokenSource.Cancel();
async void ViewCntCancelAsync(CancellationToken cancellationToken)
{
try
{
await UniTask.Delay(3000, cancellationToken: cancellationToken);
}
catch (OperationCanceledException e)
{
Debug.LogWarning("Canceled!");
}
}
参考:https://qiita.com/su10/items/ccb12742ad0be790b323
関数を2つ以上挟む
async void Hoge() {
int ms = await Hoge2();
Debug.Log("ms=" + ms); // 200
}
async UniTask<float> Hoge2() {
await UniTask.Delay(100); // ms
await UniTask.Delay(100); // ms
return 200f;
}
↓数値で指定しない場合
async void Hoge() {
await Hoge2();
}
async UniTask Hoge2() {
await UniTask.Delay(100); // ms
await UniTask.Delay(100); // ms
}
並列処理
using UnityEngine;
using Cysharp.Threading.Tasks;
public class WhenAllScene : MonoBehaviour
{
async void Start()
{
await UniTask.WhenAll(HogeAsync(), HogeAsync(), HogeAsync());
}
async UniTask HogeAsync()
{
Debug.Log("HogeAsync");
await UniTask.Delay(1000); // ms
Debug.Log("HogeAsyncEnd");
}
}
HogeAsyncがまとめて3回表示されて、一秒後に、HogeAsyncEndが3回表示される。
参考:https://light11.hatenadiary.com/entry/2021/05/27/203956
tasksでまとめて、すべて処理する
var tasks = new List<UniTask>(); tasks.Add(HogeAsync()); tasks.Add(HogeAsync()); tasks.Add(HogeAsync()); await UniTask.WhenAll(tasks);
DelayとWaitUntilのどちらか
数秒待つか、条件を満たすか、どちらかを満たすとき、処理を通す
await UniTask.WhenAny(
UniTask.Delay(10000),
UniTask.WaitUntil(() => Input.GetKeyDown(KeyCode.Return))
);
キャンセル処理
var cancellationTokenSource = new CancellationTokenSource();
await UniTask.WhenAny(
UniTask.Delay(10000, cancellationToken : cancellationTokenSource.Token),
UniTask.WaitUntil(() => Input.GetKeyDown(KeyCode.Return), cancellationToken : cancellationTokenSource.Token)
);
cancellationTokenSource.Cancel();
キャンセル処理、関数化
async UniTask DelayWaitUntil(int delayMs, Func<bool> cond)
{
cancellationTokenSource = new CancellationTokenSource();
await UniTask.WhenAny(
UniTask.Delay(delayMs, cancellationToken: cancellationTokenSource.Token),
UniTask.WaitUntil(cond, cancellationToken: cancellationTokenSource.Token)
);
}
// 関数コール
await DelayWaitUntil(1000, () => Input.GetKeyDown(KeyCode.Return));
// キャンセルしたい時に実行
cancellationTokenSource.Cancel();
参考:https://blog.gigacreation.jp/entry/2019/11/08/141305
"OperationCanceledException: The operation was canceled. "エラーを出さないようにする場合
try-catchを使う
try
{
cancellationTokenSource = new CancellationTokenSource();
await UniTask.WhenAny(
UniTask.Delay(delayMs, cancellationToken: cancellationTokenSource.Token),
UniTask.WaitUntil(cond, cancellationToken: cancellationTokenSource.Token)
);
}
catch (OperationCanceledException)
{
Debug.LogWarning("DelayCanceled!");
}
インターフェイスの書き方
asyncを使ったときの例
using Cysharp.Threading.Tasks;
public class HogeUseCase : HogeDelegate
public async UniTask Leave()
{
// 処理
}
}
using Cysharp.Threading.Tasks;
public interface HogeDelegate
{
UniTask Leave();
}
UniTaskVoidという型
asyncで、voidを返す場合は、UniTaskのvoidということで、UniTaskVoidという型がある。 UniTaskVoidのほうが明示的なので、よいかも。
- async void Hoge() {}
+ async UniTaskVoid Hoge() {}
UniTaskVoidサンプル
using Cysharp.Threading.Tasks;
async void Start()
{
Test();
}
async UniTaskVoid Test()
{
}
参考
UniTask使い方 https://qiita.com/toRisouP/items/4445b6b9bf00e49eb147
UniTask ver2 https://qiita.com/toRisouP/items/8f66fd952eaffeaf3107
