UniRxで非同期処理を扱おうとしたら失敗した話

#ゴリラ系開発者の思考
よく分からないけどUniRxで非同期処理が書けるらしい。
じゃあ、ちょっと使ってみようと思って躓いたので、駆け出しゲームクリエイターが二の轍を踏まないための情報。

「UniRX 非同期」あたりで検索すると下記の情報がヒットする。
https://qiita.com/toRisouP/items/3ced60a755ab297eb463


現在、ターン制のゲームを作成中でコンピュータ対戦を実装しようとしていた。
コンピュータが操作可能な駒を計算させる処理がやたらと重くて画面が止まってしまっていたので、
ここを非同期にしようという狙い。

#実際に使ったコード

こうすれば非同期で動いてくれるんでしょ？？
コンピュータが選択可能な駒の内、どのマスに動けるのかといった情報の全パターンを取得しようとしている。
(Context.tacticsListにどんどこ情報を詰め込もうとしている。)
とりあえず、async を使っておけば、非同期になるだろうという理解。

    private async UniTaskVoid Main()
    {
        await UniTask.Run (() =>
        {
            Debug.Log("InitializingState");
            Context.tacticsList = new List<Tactis>();

            List<PieceObject> fieldPieces = new List<PieceObject>();
            fieldPieces = StageManager.GetFieldPieceObjects();
            Debug.Log("InitializingState__:" + StageManager.GetFieldPieceObjects().Count);
            Debug.Log("InitializingState__:" + fieldPieces.Count);
            // foreach(PieceObject pieceObject in fieldPieces.Where(x => Context.IsEnemyPlayerID(x.OwnerPlayerID)))

            Debug.Log($"fieldPieces:{fieldPieces.Count}");
            foreach(PieceObject pieceObject in fieldPieces)
            {
                Debug.Log("InitializingState__1:" + pieceObject.name);
                // ピースの移動範囲をそれぞれ取得
                RangeCaluclatar rangeCaluclatar = new RangeCaluclatar();

                // 破壊可能なピースを元に考察データ取得
                Context.tacticsList.AddRange(rangeCaluclatar.GetTactisList(pieceObject));

                StateMachine.SendEvent((int)StateEventId.CheckSafety);

                Debug.Log("InitializingState__2:" + pieceObject.name);
                
            }
            Debug.Log("InitializingState__:" + Context.tacticsList.Count);
        });
    }


呼び出し元はこんな感じ。
ObserveOnMainThreadというキーワードを使っておけば、メインスレッドに戻ってこれるとかそんな理解。

    protected override void Enter()
    {
        Debug.Log("CheckSafetyState");
        Observable.Start(() =>
        {
            Debug.Log("CheckSafetyState_");
            var task = Main();
        })
        // Subscribeをスレッドプール上で実行する
        // .SubscribeOn(Scheduler.ThreadPool)
        // UnityAPIを使用するためにメインスレッドに切り替える
        .ObserveOnMainThread()
        .Select(_ => Context.gameObject.name)
        .Subscribe(Debug.Log);
    }


実際に上記を動かしてみると、取得しようとした情報は全く取得できておらず、リストのカウントは0となってしまっていた。
(Context.tacticsList.Countは0のまま。)


#非同期処理では素直にforeachを使えない

原因は、非同期処理の中でforeachを使っていることに問題があった。
非同期処理内でイテレータとなるforeachを動かすとどうにもうまく動いてくれない。

処理したい大元のリストをオブザーバブル化して、ForEachAsyncを使用して非同期処理を行う必要があるらしい。

    protected override void Enter()
    {
            Debug.Log("InitializingState");
            Context.tacticsList = new List<Tactis>();
            

            List<PieceObject> fieldPieces = new List<PieceObject>();
            fieldPieces = StageManager.GetFieldPieceObjects();
            Debug.Log("InitializingState__:" + StageManager.GetFieldPieceObjects().Count);
            Debug.Log("InitializingState__:" + fieldPieces.Count);
            // foreach(PieceObject pieceObject in fieldPieces.Where(x => Context.IsEnemyPlayerID(x.OwnerPlayerID)))

            Debug.Log($"fieldPieces:{fieldPieces.Count}");

            // fieldPiecesに対してForEachAsyncを行い、非同期で処理を行う
            // ※通常のforeachでは、非同期処理どころか処理自体が行われない。
            fieldPieces
                .ToObservable()
                .Merge()         
                .ForEachAsync(pieceObject => 
                {
                    Debug.Log("InitializingState__1:" + pieceObject.name);
                    // ピースの移動範囲をそれぞれ取得
                    RangeCaluclatar rangeCaluclatar = new RangeCaluclatar();

                    // 破壊可能なピースを元に考察データ取得
                    Context.tacticsList.AddRange(rangeCaluclatar.GetTactisList(pieceObject));

                    StateMachine.SendEvent((int)StateEventId.CheckSafety);

                    Debug.Log("InitializingState__2:" + pieceObject.name);

                })
                .Subscribe(x => Debug.Log("OnNext"));
            Debug.Log("InitializingState__:" + Context.tacticsList.Count);
    }

詳しいことは正直わかんないです。

今後勉強して追記していきます。