Posted at

UnityでUniTaskな処理を直列に並べて処理をする

この記事は、UniRxとUniTaskが前提になります。

UniRxとUniTaskついてはまずこちらをご覧ください

https://github.com/neuecc/UniRx

https://github.com/Cysharp/UniTask

昨今、Unityが対応しているC#バージョンが上がってきたことにより、UniTaskなどで非同期な処理を書くことが多くなってきました。

しかしUnity上でゲーム表現のリッチ化などもあり、リソースの都合上並列で行うことにできない非同期処理の需要は細々とあるかとおもいます。

自分もそのようなものが必要になったので、UniTaskな処理をまとめてQueueに予約し、逐次的に処理をしていくSequentialProcessQueueというクラスをGist上に公開したので紹介します。

SequentialProcessQueue

https://gist.github.com/yKimisaki/0909e811f4959badba0ad7f8b3a2ee7e

以下はアバターのあるマルチプレイなゲームで、プレイヤーアバターを作るファクトリクラスを想定したものです。


SomeFactory.cs

class PlayerAvatarFactory : IDisposable

{
SequentialProcessQueue queue;

public SomeFactory()
{
// デフォルトはObservable.EveryUpdateですが、引数で変更することができます
queue = new SequentialProcessQueue(MainThreadDispatchType.FixedUpdate);

// SequentialProcessQueueで処理が走っているかはIsProcessingで受けることができます
queue.IsProcess.Subscribe();
}

public void Dispose()
{
// 必ず不要になったらDisposeしてください
queue.Dispose();
}

// いつプレイヤーが入ってくるかわからない
public void OnJoinPlayer()
{
try
{
var cancellationTokenSource = new CancellationTokenSource();
queue.ReserveProcess(CreatePlayerAvatarAsync, cancellationTokenSource.Token); // 処理を予約

// 予約を個別にキャンセルできる
// SequentialProcessQueueで処理が開始する前にキャンセルすればスキップされる
cancellationTokenSource.Cancel();

async UniTask CreatePlayerAvatarAsync()
{
// 並列にできないとても重い処理

if (cancellationTokenSource.Token.IsCancellationRequested)
{
// 個別でキャンセルされてる場合はこっち
return;
}

if (queue.AllQueueCancellation.IsCancellationRequested)
{
// SequentialProcessQueueがDisposeされている等のキャンセルはこっち
return;
}

// 並列にできないとても重い処理
}
}
catch (OperationCanceledException e)
{
// 既にCancellationTokenSourceがキャンセルされてる場合や
// SequentialProcessQueueがDisposeされている場合は
// ReserveProcessがOperationCanceledExceptionを吐く
}
}
}