Unityに関連する記事です

 以下の内容で実装を進めていきます。

発展9 ーステージ機能の実装ー
14.ステージデータを利用して、ステージの番号に応じた MainMap ゲームオブジェクトを生成して自動的に設定を行う機能を実装する



 新しい学習内容は、以下の通りです。

 ・1つのシーンを異なる情報を使って運用する方法
 ・処理の読み解き方 ータプルによる分解代入処理ー



14.ステージデータを利用して、ステージの番号に応じた MainMap ゲームオブジェクトを生成して自動的に設定を行う機能を実装する

1.設計


 Start メソッドを利用して、GameData クラスに設定されている StageNo 変数からステージの情報 StageData クラスを取得します。
この情報を利用して、ステージ、防衛拠点の生成を行い、EnemyGenerator クラスに移動経路と生成するエネミーの情報を提供します。

 DefenseBase クラス、EnemyGenerator クラスの Start メソッドは、GameManager クラスから命令を受けてから実行されるメソッドに変更し、
GameManager クラスから命令を受けてから各クラス内の設定を行うように設計を変更しています。

 そのため、ゲームを開始する際の設定情報については、GameManager クラスの Start メソッドがすべての開始地点となり、
ここから各クラスへ設定を行うように命令を出すようにすることで、順序立てた通りに処理を実行していく作りになっています。



 処理のロジックは、スタート地点からゴール地点へ向かって、コメントを書いていくようにします。
その後、ゴール地点側から、スタート地点へ向かって、コメントに沿ったプログラムを実装するようにします。

 このようにロジックを組み立てていくことで、処理の流れを確認しながらプログラムを実装することが出来ます。

 今回であれば、処理のスタート地点は GameManager クラスです。こちらの Start メソッド内が該当します。
TODO を記述していますので、このようにコメントを書いていくといいでしょう。

 GameManager クラス内にメソッドを追加する場合には、この時点でコメントを作っておきます。
コメントは詳細に、記述したい内容を書いておくことでプログラムを作りやすくなります。

 GameManager クラス内のコメントを書き終わったら、ここから先のクラスへ展開します。
ステージデータを参照して処理をしているクラスを見直し、どのクラスに影響があるかを精査します。

 現時点では、DefenseBase クラスにある耐久力の値、EnemyGenerator クラスにあるエネミーの移動経路の情報が、
ステージデータを参照する部分になりますので、該当する変数の部分にそれぞれのクラスにコメントを記述するようにします。

 変数に代入している部分があるのであればその上にコメントを書いて、ステージデータを参照する値にする、と書いておきましょう。



 このとき重要になるのは、どのようにしてステージの情報から各値を参照するようにするかです。
これにはメソッドの引数を上手く活用していきましょう。GameManager クラスから各クラスにあるメソッドを実行する際に、
必要な情報を提供するためには、どのような処理が必要になるかを検討してみてください。

 DefenseBase クラスのように Start メソッドから始まっていて、GameManager クラスから呼び出すためのメソッドがない場合には、
新しく public 修飾子のメソッドを作成して、それを GameManager クラスから実行し、必要な情報を引数を通じて受け取れるようにしましょう。

 今回は先ほども説明したように Start メソッドは GameManager クラスのみとし、他のクラスはメソッドを実行して処理が始めるようにしてみてください。

 このようにして処理の流れをイメージし、イメージしたものをコメントにしていくことで処理の全体像を意識してください



 すべての処理がつながったら、今後は、GameManager クラスに向かって、コメントになっている部分をプログラム化していきます。
EnemyGenerator クラス、DefenseBase クラスを修正してから GameManager クラスのコメント部分をプログラムにできれば、
呼び出す対象のメソッドが完成しているため、エラーも出ずに、キレイな処理の実装が完成出来ます。


2.EnemyGenerator スクリプトを修正する


 設計に基づいて処理のロジックを組んでいきます。
設計で提示したロジックの流れと実装していく内容を1つずつ確認しながら、処理の内容を理解していきましょう。

 GenerateEnemy メソッドは、参照する情報を StageData クラスのものに変更します。
そのため修正箇所が多いですので、気を付けて処理の追加をおこなってください。


EnemyGenerator.cs

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


 スクリプトを修正したらセーブします。


3.DefenseBase スクリプトを修正する


 StageData クラスに設定されている耐久力の値を参照して、防衛拠点の耐久力を設定するように処理を修正します。

 Start メソッドを public 修飾子を持つ SetUpDefenseBase メソッドに書き換えて、引数も追加します。
このメソッドを GameManager クラス側から実行してもらって、引数を通じて情報をもらいつつ、設定を行うようにします。


DefenseBase.cs

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


 スクリプトを修正したらセーブを行います。


4.CharaGenerator スクリプトを修正する


 いままではステージがヒエラルキーにあり、かつ固定であったため、grid 変数と tilemaps 変数も最初からアサインしておくことが出来ました。

 ただし、これからはステージ自体がプレハブとなり、ゲームの実行後に生成されることになりましたので、
ステージの情報である MapInfo も動的に変化するようになりました。

 そのため、CharaGenerator において利用する Grid と Tilemap の情報も、今回のステージの情報を利用する方式に変更する必要があります。

 SetUpCharaGenerator メソッド内に TODO で残っている部分を追加して、メソッドの引数を利用して今回利用するステージの情報を受け取り、
それを使って、grid 変数と tilemaps 変数に情報を代入してステージ情報を参照して利用する方式に変更していきます。


CharaGenerator.cs

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


 スクリプトを修正したらセーブします。


<処理の読み解き方 ータプルによる分解代入処理ー>


 今回追加した処理では、SetUpCharaGenerator メソッドに第2引数を追加して、MapInfo クラスの参照を受け取っています。

 MapInfo クラスには、Timemap 型と Grid 型が宣言されていますが、どちらも private の情報です。
そのため、MapInfo クラスに用意してあるゲッターメソッドを利用して、これらの情報を取得するようにしています。

 この GetMapInfo()メソッドの戻り値はタプル型であるため、既に宣言されている変数にタプルを分解することができます。
その結果、今回のような処理を記述することで、2つの型の情報をまとめて代入することが出来ます。


<MapInfo.GetMapInfo() メソッド>
    /// <summary>
    /// マップの情報を取得
    /// </summary>
    /// <returns></returns>
    public (Tilemap, Grid) GetMapInfo() {
        return (tilemaps, grid);
    }

 SetUpCharaGenerator メソッド内では、GetMapInfo() メソッドを実行することで、
Timemap 型と Grid 型の両方の情報を取得し、変数にまとめて代入処理しています。


<今回実装している処理>
// ステージのデータを取得
(tilemaps, grid) = currentMapInfo.GetMapInfo();

 型で見ると分かりやすくなります。

// ステージのデータを取得
(Tilemap 型, Grid 型) = GetMapInfo() メソッドの戻り値 (Tilemap 型, Grid 型);

 同じ型同士の代入処理が行われていることが分かります。



 また、タプルの値を別々に受け取る書式でも取得出来ます。


<今回実装している処理の別の書式>
// ステージのデータをそれぞれに分けて取得
tilemaps = currentMapInfo.GetMapInfo().item1;  // <= item1 は、タプルの最初の値

grid = currentMapInfo.GetMapInfo().item2;    // <= item2 は、タプルの2番目の値

 この例では、必要な Timemap と Grid の値を1つの情報として取り出して、代入処理を行っています。

 どちらの処理でも問題ありません。



 また、タプルの書式には、他の方法もあります。

 例えば、ローカル変数を用意して、GetMapInfo() メソッドを1回だけ実行するケースです。

<別の実装例 
(Tilemap, Grid) currentTileData = currentMapInfo.GetMapInfo();

// ステージのデータを取得
tilemaps = currentTileData.item1;

grid = currentTileData.item2;



 タプルには各変数名をつけることが出来るため、下記の方法でも実装出来ます。

<別の実装例◆
(Tilemap currentTilemap, Grid currentGrid) currentTileData = currentMapInfo.GetMapInfo();

// ステージのデータを取得
tilemaps = currentTileData.currentTilemap;

grid = currentTileData.currentGrid;

 その場合、item ではなく、タプル内で作成した変数を使うことが出来ます。



 バリュータプルの機能が利用できる場合、MapInfo の GetMapInfo() メソッドの戻り値に変数を定義できます。


<MapInfo.GetMapInfo() メソッドの戻り値にバリュータプルを利用した場合>
    /// <summary>
    /// マップの情報を取得
    /// </summary>
    /// <returns></returns>
    public (Tilemap currentTilemap, Grid currentGrid) GetMapInfo() {    //  ← 戻り値に変数名を定義できる
        return (tilemaps, grid);
    }

 その場合、情報を取り出すときに、item ではなく、変数名で指定できます。


// ステージのデータを取得
tilemaps = currentMapInfo.GetMapInfo().currentTilemap;

grid = currentMapInfo.GetMapInfo().currentGrid;

 いずれの方法でも実装可能ですので、実際に使う処理は1つですが、読み解き方は覚えておくとよいでしょう。


参考サイト
MicroSoft
タプルとその他の型の分解


5.GameManager を修正する


 Start メソッドを利用して、GameData クラスに設定されている StageNo 変数からステージの情報 StageData クラスを取得します。
この情報を利用して、キャラ配置用のポップアップにステージの情報を引数を通じて提供します。(そのため、メソッドの引数を追加しています。)

 また、ステージ、防衛拠点の生成を行い、EnemyGenerator クラスに移動経路と生成するエネミーの情報を提供します。

 DefenseBase クラス、EnemyGenerator クラスの Start メソッドは、GameManager クラスから命令を受けてから実行されるメソッドに変更し、
あくまでも、GameManager クラスから命令を受けてから設定を行うように設計を変更しています。

 そのため、ゲームを開始する際の設定情報については、GameManager クラスの Start メソッドがすべての開始地点となり、
ここから各クラスへ設定を行うように命令を出すようにすることで、順序立てた通りに処理を実行していくようになっています。

 処理の追加・修正が多く、また多くのクラスにまたがって処理が進んでいくため、
しっかりと処理の流れを追いかけて読み解き、どのようにして処理が動いているかを把握するようにしてください。


GameManager.cs


 スクリプトを修正したらセーブをします。


6.GameManager ゲームオブジェクトの設定を行う


 DefenseBasePrefab 変数に、Prefabs フォルダ内にある DefenseBase ゲームオブジェクトのプレファブをドラッグアンドドロップしてアサインしてください。

 currentMapInfo 変数と currentStageData 変数は、ゲームの実行時に GameData クラスに設定した StageNo 変数から参照された StageData クラスから情報が代入されますので、
この部分は空のままで問題ありません。


インスペクター画像



 以上で設定は完了です。


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



 GameData クラスの StageNo 変数を設定し、ゲームを実行します。
空になっていた GameManager クラスの currentStageData 変数にステージの情報が、スクリプタブル・オブジェクトより参照されて代入されれば制御成功です。
その後、その情報を参照して MainMap ゲームオブジェクトと DefenseBase ゲームオブジェクトが生成されて、EnemyGenerator クラスに移動経路の情報が登録されれば制御成功です。

 複数のクラスに情報が設定されるようになりますので、何度もゲームの実行をして、どの部分の処理が制御されているかをしっかり把握しておいてください。


GameManager ゲームオブジェクト インスペクター画像



GameManager ゲームオブジェクト インスペクター画像





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

 次は 発展10 ーエフェクト機能の準備− です。

コメントをかく


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

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

Menu



技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

3Dレールガンシューティング(応用編)

3D脱出ゲーム(抜粋)

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

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

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

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

VideoPlayer イベント連動の実装例

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

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

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

private



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

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