はじめに
今回はUniTask-SupplementというUniTaskのCancellationTokenを渡す記述をより簡単にしてくれるライブラリについて紹介をしたいと思います。
導入方法
PackageManagerのAdd package from git url...を選択し、以下の文字列を打ち込みます。
https://github.com/su10/UniTask-Supplement.git#upm

OpenUPMにも対応しているので、そちらのやり方がいい場合はGitHubをチェックしてください。
github.com
使い方
基本
Cysharp/UniTaskではCancellationTokenを名前付き引数として渡さなければならなかったものを、省略できるようになります。
var cancellationToken = this.GetCancellationTokenOnDestroy(); // 以下コメントアウトされている書き方がUniTask Supplementを利用しない書き方 //await UniTask.DelayFrame(1, cancellationToken: cancellationToken); await UniTask.DelayFrame(1, cancellationToken); //await UniTask.Delay(1, cancellationToken: cancellationToken); await UniTask.Delay(1, cancellationToken); //await UniTask.Delay(TimeSpan.FromMilliseconds(1), cancellationToken: cancellationToken); await UniTask.Delay(TimeSpan.FromMilliseconds(1), cancellationToken); //await UniTask.WaitUntil(() => true, cancellationToken: cancellationToken); await UniTask.WaitUntil(() => true, cancellationToken); //await UniTask.WaitWhile(() => false, cancellationToken: cancellationToken); await UniTask.WaitWhile(() => false, cancellationToken); //await UniTask.WaitUntilValueChanged(transform, x => x.position, cancellationToken: cancellationToken); await UniTask.WaitUntilValueChanged(transform, x => x.transform, cancellationToken);
実装としてはCancellationToken = defaultのようにデフォルト値が設定されていないオーバーロードを増やしています。
ちゃんとAssembly Definition Referenceを利用してUniTask(後で紹介するがUniTask.DoTweenも)のアセンブリにコードを入れてくれていますね。助かります。
新しく実装されたメソッド
UniTask.DelayMilliseconds・UniTask.DelaySecondというメソッドが新しく追加されました。
// UniTask.DelayMillisecondsはUniTaskSupplementの中に実装されている(Cysharp/UniTaskには実装されていない) await UniTask.DelayMilliseconds(1, cancellationToken: cancellationToken); await UniTask.DelayMilliseconds(1, cancellationToken); // UniTask.DelaySecondはUniTaskSupplementの中に実装されている(Cysharp/UniTaskには実装されていない) await UniTask.DelaySeconds(1, cancellationToken: cancellationToken); await UniTask.DelaySeconds(1, cancellationToken);
またシンボル定義を行うことによって、これらのメソッドを利用させないようにすることもできます。
// UniTask.DelayMillisecondsを消す場合
UNITASK_SUPPLEMENT_DISABLE_DELAY_MILLISECONDS
// UniTask.DelaySecondsを消す場合
UNITASK_SUPPLEMENT_DISABLE_DELAY_SECONDS
Unityでプロジェクト全体にシンボル定義を行う場合はProjectSettings/Player/OtherSettings/Script Compilation/Scripting DefineSymbolsに記述してあげればOKです。

またアセンブリ単位で定義したい場合はAssembly Definition FileのVersion Definesで記述してあげます。

【Unity】asmdefのVersion Definesを利用して特定のパッケージ(の特定バージョン)がある場合にのみシンボル定義を行う - はなちるのマイノート
WhenAnyでCancellationTokenを渡す
WhenAnyにもCanellationTokenを渡せるようになります。
private async void Start() { var token = this.GetCancellationTokenOnDestroy(); // 非同期メソッドの返り値の型(UniTask<int>とUniTask<int>)が同じ場合 var (winArgumentIndex, result) = await UniTask.WhenAny<int>( cancel => HogeAsync(cancel), cancel => HogeAsync(cancel), token ); // 非同期メソッドの返り値の型(UniTask<int>とUniTask<bool>)が異なる場合 var (winArgumentIndex2, result1, result2) = await UniTask.WhenAny( cancel => HogeAsync(cancel), cancel => FugaAsync(cancel), token ); } private async UniTask<int> HogeAsync(CancellationToken token) { await UniTask.DelayMilliseconds(10, token); return 1; } private async UniTask<bool> FugaAsync(CancellationToken token) { await UniTask.DelayMilliseconds(10, token); return true; }
ただし挙動についてちゃんと理解しながら利用した方が良いでしょう。
どんな挙動をするのか実装を見てみます。
public static async UniTask<(int winArgumentIndex, T result)> WhenAny<T>( Func<CancellationToken, UniTask<T>> taskFunc1, Func<CancellationToken, UniTask<T>> taskFunc2, CancellationToken cancellationToken ) { var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); (int winArgumentIndex, T result) result = default; try { result = await WhenAny<T>( taskFunc1(cts.Token), taskFunc2(cts.Token) ); cts.Cancel(); } catch (OperationCanceledException ex) when (ex.CancellationToken == cts.Token) { if (cancellationToken.IsCancellationRequested) { throw new OperationCanceledException(ex.Message, ex, cancellationToken); } throw; } finally { cts.Dispose(); } return result; }
UniTask-Supplement/UniTask.WhenAny.Generated.cs at main · su10/UniTask-Supplement · GitHub
エラーが発生しなければ以下のステップを踏みます。
- 引数の
CancelltionTokenを紐づけながらCancellationTokenSourceを生成 - 引数の
UniTask<T>を生成したCancellationTokenSource.Tokenを渡しながら実行 - エラーなく終了すれば
CancellationTokenSource.Cancellを呼び出す
つまり引数で渡したUniTask達は、どれか一つのUniTaskが終われば他もキャンセルされるようになっています。(ちゃんとUniTaskの中で止まるように書いてあればであるが)
使わないのに処理が続いているなんてことが起こらないようになるので、助かる機能だと思います。
またキャンセル周りの実装について、以下の記事が参考になりました。
neue.cc
DoTween関係
UniTask.DoTweenの機能を拡張する機能も実装されています。
UniTaskのDoTweenの機能を有効にするには少し作業が必要でした。
【Unity】DoTweenでUniTaskを対応させてawaitできるようにする - はなちるのマイノート
具体的にはUNITASK_DOTWEEN_SUPPORTをシンボル定義する必要があったわけですが、UniTask-Supplementの機能を有効にする場合は代わりにUNITASK_SUPPLEMENT_DOTWEEN_SUPPORTを定義します。
TweenerCore<float, float, FloatOptions> tween = DOTween.To( () => 0f, x => Debug.Log(x), 1f, 1f ); CancellationToken cancellationToken = this.GetCancellationTokenOnDestroy(); // UniTask await tween.ToUniTask(cancellationToken: cancellationToken); await tween.AwaitForComplete(cancellationToken: cancellationToken); await tween.AwaitForPause(cancellationToken: cancellationToken); await tween.AwaitForPlay(cancellationToken: cancellationToken); await tween.AwaitForRewind(cancellationToken: cancellationToken); await tween.AwaitForStepComplete(cancellationToken: cancellationToken); // with UniTask Supplement await tween.ToUniTask(cancellationToken); await tween.AwaitForComplete(cancellationToken); await tween.AwaitForPause(cancellationToken); await tween.AwaitForPlay(cancellationToken); await tween.AwaitForRewind(cancellationToken); await tween.AwaitForStepComplete(cancellationToken);
実装としてCysharp/UniTaskに同梱されていたDOTweenAsyncExtensions.csがまるっとUniTask-Supplementに含まれており、完全上位互換になっているので安心してください。
新しく追加された機能はDoTweenAsyncExtensions.Supplement.csの中に入っています。
#if UNITASK_SUPPLEMENT_DOTWEEN_SUPPORT using System.Threading; using DG.Tweening; namespace Cysharp.Threading.Tasks { public static partial class DOTweenAsyncExtensions { private const TweenCancelBehaviour DefaultTweenCancelBehaviour = #if UNITASK_SUPPLEMENT_DOTWEEN_SUPPORT_USE_ORIGINAL_DEFAULT_TWEEN_CANCEL_BEHAVIOUR TweenCancelBehaviour.Kill; #else TweenCancelBehaviour.KillAndCancelAwait; #endif public static UniTask ToUniTask(this Tween tween, CancellationToken cancellationToken) { return ToUniTask(tween, DefaultTweenCancelBehaviour, cancellationToken); } public static UniTask AwaitForComplete(this Tween tween, CancellationToken cancellationToken) { return AwaitForComplete(tween, DefaultTweenCancelBehaviour, cancellationToken); } public static UniTask AwaitForPause(this Tween tween, CancellationToken cancellationToken) { return AwaitForPause(tween, DefaultTweenCancelBehaviour, cancellationToken); } public static UniTask AwaitForPlay(this Tween tween, CancellationToken cancellationToken) { return AwaitForPlay(tween, DefaultTweenCancelBehaviour, cancellationToken); } public static UniTask AwaitForRewind(this Tween tween, CancellationToken cancellationToken) { return AwaitForRewind(tween, DefaultTweenCancelBehaviour, cancellationToken); } public static UniTask AwaitForStepComplete(this Tween tween, CancellationToken cancellationToken) { return AwaitForStepComplete(tween, DefaultTweenCancelBehaviour, cancellationToken); } } } #endif
UniTask-Supplement/DOTweenAsyncExtensions.Supplement.cs at main · su10/UniTask-Supplement · GitHub
またDoTweenAsyncExtensions.Supplement.csではTweenCancelBehaviourなるものがUniTask.DoTweenのデフォルトのものと異なっています。
private const TweenCancelBehaviour DefaultTweenCancelBehaviour = #if UNITASK_SUPPLEMENT_DOTWEEN_SUPPORT_USE_ORIGINAL_DEFAULT_TWEEN_CANCEL_BEHAVIOUR TweenCancelBehaviour.Kill; #else TweenCancelBehaviour.KillAndCancelAwait; #endif
こちらをデフォルトと同じ挙動にしたい場合はUNITASK_SUPPLEMENT_DOTWEEN_SUPPORT_USE_ORIGINAL_DEFAULT_TWEEN_CANCEL_BEHAVIOURを定義してとのことです。
GitHub - su10/UniTask-Supplement: Supplemental codes for UniTask.
具体的にどう異なるかは以下のコードの箇所が関係しそうです。
void OnUpdate() { originalUpdateAction?.Invoke(); if (!cancellationToken.IsCancellationRequested) { return; } switch (this.cancelBehaviour) { case TweenCancelBehaviour.Kill: default: this.tween.Kill(false); break; case TweenCancelBehaviour.KillAndCancelAwait: this.canceled = true; this.tween.Kill(false); break; // 以下省略 } }