Unityに関連する記事です

 前回作成した機能を拡張し、複数のイベントを順番に制御するイベント処理の実装例です。

 この機能も処理の抽象化を活用することにより実装が可能です。



<実装動画 宝箱のイベントが終了してから、オブジェクト生成イベントが順番に実行される>
動画ファイルへのリンク



 アサインした GameEvent によりイベントの順番が制御できます。

 そのため、アサインする順番を逆にすることで、オブジェクト生成→宝箱の順にイベント制御が可能です。


<実装動画 オブジェクト生成イベントが終了してから、宝箱のイベントが順番に実行される>
動画ファイルへのリンク



<学習内容>
 ・コルーチンの内部から戻り値のように値を取り出す方法



設計


 親クラスである GameEventBase クラスをそのまま活用し、子クラスを新規作成してイベントの処理を作成してきます。
現在は会話イベント用のクラスを作成していますが、他の分岐処理の部分も1つずつイベント化(子クラス化)していきます。

 GameEvent を実行している GameEvetHandler クラスを修正し、1つしか登録できない GameEvent 変数を配列に変更し、
複数の GameEventBase クラスをアサインして登録出来るようにします。

 それに合わせて、現在は1回しか実行していない GameEvent の実行命令を修正し、
登録している GameEvent を上から順番に実行していくように修正します。


インスペクター画像例



 またインスペクターにある配列の情報はドラッグアンドドロップで並び替えができます。
そのため、この部分を変更するだけで、イベント処理の実行順を変えることが出来ます。
これは新しい子クラスを作成しても同様です。


イベント実行順の変更
動画ファイルへのリンク

 このような設計になっているため、イベントの順番を変えるためにスクリプトへの処理の変更は必要ありません。


新しいイベント用の子クラスの作成


 前回作成した GameEventNpcTalk クラスと同じように、イベントにつき1つのクラスを作成します。

 ここではアイテム取得関連のイベントを2つ記載します。


1.宝箱のイベント用


 前回作成済の子クラスは引き続き利用できますので、ここでは他の子クラスを作成します。
いずれも親クラスである GameEventBase クラスを継承した子クラスを作成します。

 そして GameEventNpcTalk クラスと同じように、親クラスの ExecuteEventCoroutine メソッドをオーバーライドして
それぞれのイベントの処理を記述します。宝箱であれば、宝箱を開けた際の処理を記述します。

 ここでは既存の TreasureBox クラスを元に新しい子クラスを作成し、イベント機能に対応させています。
クラスの命名規則はありませんが、GameEvent 〜 という形で統一すると、イベント用のクラスであることが分かりやすくなります。


GameEventTreasureBox.cs

<= クリックすると開きます。



2.宝箱以外からのアイテムの取得用


 宝箱を通さずにアイテムを入手するようなケースのイベント処理です。
そのためこれは会話イベントなどと組み合わせて使うことを想定しています。


GameEventGetItem.cs

<= クリックすると開きます。



 長くなるため、他の子クラスについては別のページにまとめて置きます。


命令を受ける側の機能の修正


 最初に説明があったように、GameEventHandler クラスを修正し、複数の GameEventBase クラスを登録できるようにした上で、
登録した順番通りに1つずつ GameEvent が実行できるように処理を修正します。

 元からあるメソッドを修正してもよいのですが、メソッドの内容をほぼ書き換えることになるため、
新しい変数とメソッドを作成して対応します。


<= クリックすると開きます。


 処理の内容が難しいため、1つずつ、どのような処理を行っているのかを調べながら学習し、復習しながら覚えてください。


<コルーチンの内部から戻り値のように値を取り出す方法>


 コルーチンメソッドの戻り値の型は IEnumerator インターフェースです。こちらは変更することが出来ません。

 そのため通常のメソッドのように戻り値の型を指定し、メソッドから戻り値を受け取ることが出来ません。

 ただし、IEnumerator インターフェースには object 型の Current プロパティが用意されています。


System.Collections.IEnumerator インターフェースの定義
namespace System.Collections
{
    public interface IEnumerator
    {
        object Current { get; }

        bool MoveNext();

        void Reset();
    }
}



 この Current プロパティには yield return 処理を利用することで情報を代入しておくことが出来ます。

 下記の GameEventGetItem クラス内のメソッドでは3箇所で利用されています。

    /// <summary>
    /// コルーチンによる非同期
    /// </summary>
    /// <returns></returns>
    public override IEnumerator ExecuteEventCoroutine() {

        // すでに所持しているアイテムの場合、セーブ処理はせず、獲得処理もしない
        if (GameData.instance.itemInventryDatasList.Exists(x => x.itemName == itemName)) {
            yield return false;  // ← ここ
            yield break;
        }
        
        // 今回終了した会話イベント後に紐づいたイベントがあるか確認
        if (AdvEngineController.instance.AdvEngine.Param.GetParameterBoolean(utageParamBoolName.ToString())) {
            GameData.instance.AddItemInventryData(itemName);

            yield return true;  // ← ここ
            yield break;
        }

        yield return false;   // ← ここ
    }

 これは他のイベント用の子クラスにも用意されています。

 いずれも bool 値を yield return で Current に代入して保持していますが、
この値を代入しているタイミングがポイントです。

 この値はセーブの可否として利用するために用意されています。
そのため、セーブの可否判定が決定したタイミングで true / false を代入しています。



 この Current の機能を活用し、IEnumerator インターフェース型の変数を用意し、この中にコルーチンメソッドの処理を代入します。

 その後、この代入されている変数を使ってコルーチンメソッドを実行します。
このように通常のメソッド処理をそのまま実行するのではなく、一度、別の変数に入れて、
その変数を使って代入されているメソッドを実行することをラップ処理といいます。

 変数を通じてコルーチンメソッドを実行し、その内部にして yield return 処理することにより、
object 型の情報としての情報として Current プロパティに値が代入されて保持されます。
この値を取り出すことで疑似的なコルーチンメソッドの戻り値として活用することが出来ます。


      // イベント処理用のコルーチンメソッドを変数に代入(ラップする)
            IEnumerator gameEventCoroutine = gameEvent.ExecuteEventCoroutine();

      // イベントの起動(実行)。このイベントが終了するまで処理は待機し、次のイベントは実行されないことを保証する
            yield return StartCoroutine(gameEventCoroutine);
            
            // このフラグを入れないと、一度 true になったあとに false に戻される可能性がある
            if (!isSaveOn && gameEventCoroutine.Current != null) { 

        // コルーチン内部から戻り値を取得するテクニック
                isSaveOn = (bool)gameEventCoroutine.Current;  //  ← ここでコルーチンメソッド内の Current プロパティからメソッド内の情報を取り出している
            }

 Current プロパティは object 型です。そのため、利用したい型に型変換(キャスト)する必要があります。

 ここではコルーチンメソッド内で true / false の値を yield return しているので、
その情報を object 型で取りだしてから (bool) を利用して 0bject → bool に型変換して利用しています。

 今回のプログラムの場合、すべてのイベント処理内で、yield return の処理を行い、true / false の値を Current に保持しています。
各子クラスの処理を見直してみてください。

 そしてその情報をイベントを実行した後で確認し、今回実行したイベントにおいて、
セーブが必要になったのか、あるいは不要なのかフィードバックさせるための機能として役立てています

 このような形で、実行したイベントからセーブ可否のフィードバックの仕組みを作ることにより、セーブが必要な時にだけセーブを行うような構造にしています。


参考サイト
【ハルシオンブログ】様
Unityのコルーチンで値を返す方法。いくつか方法あるぽいけど、その一つを紹介。



命令を実行する側の機能の修正


 PlayerController クラスの PlayActionCoroutine メソッド内にあるイベントを実行する処理を変更します。

 いままでは GameEventHandler クラスの GameEvent を1つだけ実行していましたが、
この部分を新しく作成した複数の GameEvent を実行するためのメソッドの呼び出しに変更します。


PlayerController.cs
<修正>

<= クリックすると開きます。




 またセーブの可否を確認して、セーブを行うためのメソッドを追加します。

 ここでは、イベントの内容によってセーブの可否を戻り値として受け取っておき、
その状態を確認してから、必要に応じてセーブを行います。

 PlayerController クラス内であればメソッドの追加場所はどこでも構いません。


PlayerController.cs
<追加>

<= クリックすると開きます。



イベントの作成方法


 GameEventHandler クラスをイベントを発火させたいゲームオブジェクトにアタッチし、
合わせて、そのゲームオブジェクトに実際に発火するイベント用の子クラスをアタッチします。
 
 アタッチする子クラスの順番は順不同です。
ただし、GameEvents 変数にアサインしている順番にイベントが実行されますので、アサイン順には注意してください。

 宝箱のオブジェクトに、宝箱からアイテムの入手イベントとキャラの自動生成イベントをセットしている場合には、次のようになります。


インスペクター画像例



 その前、まずはイベントのデバッグを行います。


イベントの挙動確認とエラー対策


 実際に複数のイベントを起動させる前に、まずは、1つずつ順番にアサインしてイベント処理が正常に動作するかを確認してください
その際、空っぽのアサイン場所は削除してください

 現在のプログラムではイベントの null チェックをしていないので、いずれかの要素(Element)に空っぽのアサイン場所があるとエラーが出ます
配列にはアサインがある前提です。


エラーになる場合



エラーになる場合



エラーになる場合



エラーになる場合



正常に1つずつ動作検証する場合




 どうすればエラーにならないのか、エラーになってしまう理由はなんなのか、ということも
プログラムを読み解いていくことで理解が深まり、対処方法が学習出来ます。

 特に自作する場合、どのようなエラーが発生しうるのか、自分で理解しておかなければ適切な処理が書けません。

 折角ですので、エラー対策も考えてみてください。
どのクラスに、どのような処理を施せばいいのか、プログラムを見ながら考えてみてください。


<回答例>

<= クリックすると開きます



ゲームを実行して動作を確認する


 正常に動作することを確認できたら、GameEvents 変数に2つ以上のイベントをアサインしてください。
GameEvents 変数にアサインしている順番にイベントが実行されますので、実行して確認してください。

 前のイベントが終了するまでは次のイベントは発生しません
また、プレイヤーの移動操作も出来ません

 そのような挙動になっているかを確認してください。


<実装動画 宝箱のイベントが終了してから、オブジェクト生成イベントが順番に実行される>
動画ファイルへのリンク



 アサインした GameEvent によりイベントの順番が制御できます。
逆にして、オブジェクト生成→宝箱のイベントになるか、確認してください。


インスペクター画像(GameEvents 変数へのアサインの順番を変える)



<実装動画 オブジェクト生成イベントが終了してから、宝箱のイベントが順番に実行される>
動画ファイルへのリンク



 以上でこの手順は終了です。

 イベント用の子クラスのサンプルは次の手順にあります。

 次の手順は 処理の抽象化による実装例 です。

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu



プログラムの基礎学習

コード練習

技術/知識(実装例)

2Dおはじきゲーム(発展編)

2D強制横スクロールアクション(発展編)

3Dダイビングアクション(発展編)

2Dタップシューティング(拡張編)

レースゲーム(抜粋)

2D放置ゲーム(発展編)

3D脱出ゲーム(抜粋)

2Dリアルタイムストラテジー

2Dトップビューアドベンチャー(宴アセット使用)

3Dタップアクション(NavMeshAgent 使用)

2Dトップビューアクション(カエルの為に〜、ボコスカウォーズ風)

3Dトップビューアクション(白猫風)

VideoPlayer イベント連動の実装例

VideoPlayer リスト内からムービー再生の実装例(発展)

AR 画像付きオブジェクト生成の実装例

AR リスト内から生成の実装例(発展)

private



このサイト内の作品はユニティちゃんライセンス条項の元に提供されています。

管理人/副管理人のみ編集できます