質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.53%

  • C#

    7066questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • Unity

    3966questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity3D

    1283questions

    Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

  • ゲーム開発

    166questions

  • 非同期処理

    107questions

    非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

【Unity】コルーチン内でコルーチンを連続して呼び出す【UniRx】

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 285

vuvu

score 5

 前提・実現したいこと

現在UniRxで敵ユニットのAI部分を作っています。ゲーム内容はシミュレーションRPGのようなターン制で、
敵のターンに移ると敵ユニットが順番に行動をする予定です。

使用側は以下のような形で敵をActionAIコルーチンで行動させて、終わり次第終了処理をしてから次の敵のAIを起動したいと考えています。

/* EnemyManager */
Observable.FromCoroutine(_ => enemies[i].ActionAI())
    .Subscribe(_ =>
    {
        // 敵行動終了時の処理
        // その後、次の敵行動へ
    });

 発生している問題

敵AIは[移動]→[攻撃]という流れで行動させるつもりで、[移動]と[攻撃]は既に実装できました。
[移動][攻撃]はそれぞれアニメーションなどに合わせて終了を通知させたいのでコルーチンでの非同期処理にしました。
ですが、以下のようなコードでは[移動]の完了時にActionAiの終了通知が流れてしまい、一連のコルーチンの完了を通知できていません。(移動完了時点で次の敵が移動してしまう)
[攻撃]まで終了してからActionAIの終了を通知するにはどうすればいいでしょうか?

    public IEnumerator ActionAI()
    {
        // 移動
        yield return Observable.FromCoroutine(_ => ExecuteMoveAI())
            .Do(_ => Debug.Log("移動完了"))
            .ToYieldInstruction();

        // 攻撃
        yield return Observable.FromCoroutine(_ => ExecuteAttackAI())
            .Do(_ => Debug.Log("攻撃完了"))
            .ToYieldInstruction();
    }

 

UniRxもコルーチンについても無学なため、基本的な考え方から合っているかわかりません。
以上の問題について解決策、別の実装方などアイデアを頂ければ幸いです。よろしくお願いします。

 補足情報

Unity Ver.2017.2.0f3

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

私もUniRxについて経験が浅いので自信がないのですが、EnemyManager中のFromCoroutineの第2引数publishEveryYieldがfalseなら、コルーチンが完了してからSubscribeに記述された敵行動終了時の処理が実行されるのではないかと思います。
publishEveryYieldはデフォルトでfalseなので、敵行動終了時の処理はご質問者さんの現状のコードで正しく実行されるような気がするのですが、それなのに次の敵が変なタイミングで行動を開始してしまうとなると、ご提示のコードでは省略されている、各敵の全行動完了を待機するロジックに何かトラブルがあるのではないでしょうかね...?
コード中の各段階で細かくLogを出力させてみたり、デバッガで動きを追ってみる必要があるかもしれません。

※試しにその全行動完了待機の部分をToYieldInstructionで作ったYieldInstructionをyield returnで返して待機させるようにして、Subscribeで記述していた敵行動終了時の処理はDoでやるようにしたところ、とりあえずちゃんとした順序で動いているように見えました。

Enemy

using System.Collections;
using UniRx;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public IEnumerator ActionAI()
    {
        yield return Observable.FromCoroutine(_ => this.ExecuteMoveAI())
            .Do(_ => Debug.LogFormat("ActionAI : {0} 移動完了", this.name))
            .ToYieldInstruction();

        yield return Observable.FromCoroutine(_ => this.ExecuteAttackAI())
            .Do(_ => Debug.LogFormat("ActionAI : {0} 攻撃完了", this.name))
            .ToYieldInstruction();
    }

    private IEnumerator ExecuteMoveAI()
    {
        Debug.LogFormat("ExecuteMoveAI : {0} 移動開始", this.name);

        yield return new WaitForSeconds(1.0f);

        Debug.LogFormat("ExecuteMoveAI : {0} 移動終了", this.name);
    }

    private IEnumerator ExecuteAttackAI()
    {
        Debug.LogFormat("ExecuteAttackAI : {0} 攻撃開始", this.name);

        yield return new WaitForSeconds(1.0f);

        Debug.LogFormat("ExecuteAttackAI : {0} 攻撃終了", this.name);
    }
}

EnemyManager

using System.Collections;
using UniRx;
using UnityEngine;

public class EnemyManager : MonoBehaviour
{
    public Enemy[] enemies;

    private IEnumerator Start()
    {
        Debug.Log("Start : 敵集団行動開始");

        yield return new WaitForSeconds(1.0f);

        var enemyCount = this.enemies.Length;

        for (var i = 0; i < enemyCount; i++)
        {
            var enemy = this.enemies[i];

            yield return Observable.FromCoroutine(_ => enemy.ActionAI())
                .Do(_ => Debug.LogFormat("Do : {0} 全行動終了、次の敵の行動に移る", enemy.name))
                .ToYieldInstruction();

            Debug.Log("Start : 次の敵の行動に移る");

            yield return new WaitForSeconds(1.0f);
        }

        Debug.Log("Start : 敵集団行動終了");
    }
}

コンソールに出力された結果
コンソール

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/28 10:41

    回答ありがとうございます!わざわざ実行結果まで載せていただき大変分かりやすかったです!
    今一度ソースコードを確認してみたところ、省略していたAttackAIのコルーチンでyield returnの記述が抜けていました…
    AttackAIを修正した所、無事にやりたかった挙動で実装できました!

    丁寧に回答して頂いたのに基礎的な部分のミスで申し訳ないです…
    非常に助かりました!ありがとうございました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.53%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C#

    7066questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • Unity

    3966questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity3D

    1283questions

    Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

  • ゲーム開発

    166questions

  • 非同期処理

    107questions

    非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。