Unityに関連する記事です

 プレファブにしたエネミーのゲームオブジェクトを、スクリプトを利用して自動生成する処理を実装します。
まずは最初は同じ位置から生成されるように制御を行います。
その処理を実装後、生成後の移動開始位置を X 軸だけランダムに変更し、左右の異なる位置から移動開始するように制御を行います。
 
以下の内容で順番に実装を進めていきます。


<実装動画 エネミーのプレファブから自動生成して左右のランダムな位置から移動させる>
動画ファイルへのリンク


手順18 −エネミーの自動生成処理の実装−
36.EnemyGenerator ゲームオブジェクトを作成し、設定する
37.EnemyGenerator スクリプトを作成して、プレファブ化してあるエネミー用ゲームオブジェクトの自動生成処理を追加する
38.EnemyGenerator スクリプトと EnemyController スクリプトを修正し、エネミーの移動開始位置を X 軸だけランダムに変更し、左右の異なる位置から移動開始するように制御する



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

 ・Start メソッドの役割を持つメソッドを作成して利用する方法
 ・複数行の処理を1行にまとめて記述する方法



36.EnemyGenerator ゲームオブジェクトを作成し、設定する

1.設計


 この手順ではエネミーのプレファブを自動生成するスクリプトを作成していきますが、そのスクリプトをアタッチするための生成役のゲームオブジェクトの設計を考えます。

 EnemyGenerator ゲームオブジェクトという、役割が明確になる名前を付けて作成し、利用する設計にします。
このゲームオブジェクトは自動生成用のスクリプトをアタッチする他、実際にエネミーのゲームオブジェクトが生成される位置情報としても利用するようにします。
 
 エネミーのゲームオブジェクトは Canvas 内で移動するように設計されてプレファブ化してあります。
そのため、この EnemyGenerator ゲームオブジェクトも Canvas ゲームオブジェクトの子オブジェクトとして作成する必要があります。

 それではゲームオブジェクトの作成と位置の設定を行います。


2.EnemyGenerator ゲームオブジェクトを作成する


 Canvas ゲームオブジェクトの上で右クリックをしてメニューを開き、Create Empty を選択します
新しく空のゲームオブジェクト(Transfrom コンポーネントのみアタッチされている、役割のまだないゲームオブジェクト)が作成されますので、名前を EnemyGenerator に変更します。
EnemyGenerator ゲームオブジェクトは、エネミーを自動生成し、生成位置の情報として利用する役割を持つゲームオブジェクトです。


ヒエラルキー画像



 EnemyGenerator ゲームオブジェクトを選択して、RectTransform コンポーネントがアタッチされているか確認します。
Canvas ゲームオブジェクトに含まれるオブジェクトは Transform コンポーネントではなく、RectTransform コンポーネントによって位置情報を管理しています
もしも通常の Transform コンポーネントがアタッチされている場合には、再度作り直してください。


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



EnemyGenerator ゲームオブジェクト Sceneビュー画像



 続いてゲームオブジェクトの設定を行います。


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


 EnemyGenerator ゲームオブジェクトの位置を調整します。
今回は、このゲームオブジェクトの位置を、エネミーの生成する位置と利用します。

 まずは最初に、アンカーの設定を行い、画面上中央(Top Center)にゲームオブジェクトのアンカーを設定します。
アンカーの設定方法は学習済です。もしも忘れてしまったら、前の手順を見返して復習しながら実装してください。

 アンカー設定後、自分のゲーム画面の解像度に合わせて、EnemyGenerator ゲームオブジェクトがゲームの画面外に出るようにしてください。

 Sceneビューに設置する際に、Game 画面よりも外に設置することで、ゲームの外からエネミーが画面の中に降りてくるように演出させることが出来ます。


EnemyGenerator ゲームオブジェクト インスペクター画像(参考値)



EnemyGenerator ゲームオブジェクト Sceneビュー画像



EnemyGenerator ゲームオブジェクト Sceneビュー画像



 以上でゲームオブジェクトの設定は完了です。


37.EnemyGenerator スクリプトを作成して、プレファブ化してあるエネミー用ゲームオブジェクトの自動生成処理を追加する

1.設計


 今までも色々なゲームで自動生成を行う処理を実装してきていると思いますので、それを思い出しながら実装手順を考えてみてください。

 どのような情報があれば、プレファブのクローンを生成することが出来るのか、生成させる処理はどのように記述するのか、など、基本的な処理は同じです。

 設計として必要な部分は、それ以外の部分も考えていきます。

 まず、どの位のタイミングでエネミーのプレファブのクローンを生成するようにするのか、という設定情報が必要になります。
今回はこの条件として、「一定時間経過後」という設定にします。そうなると、「一定時間」を判断するための設定値の変数と、その時間を計測するための変数の2つの情報が必要になります。

 一定時間、という値を計測するためには、どんな処理を、どの場所(メソッド)に記述すればよいでしょうか。
また、一定時間が経過後には、再度、一定時間を計測するようにしなければ1回だけ生成して終了になってしまいます。
自動生成を行うためには、一定時間でエネミーを生成後、この処理を繰り返していくサイクルがないと実装できないでしょう。

 一定時間が経過したら、とは、準備時間(待機時間)を超えたら、とも言えます。
考え方を1つに限定せず、色々な角度から考えて、日本語をプログラムのロジックに近い言葉に変えていくと、ロジックを組みやすくなります。

 処理をメソッドに分けて記述が難しければ、まずはすべての処理を記述してから、今まで学習してきたように、後で処理をメソッド化してください。

 まずはイメージした処理を、日本語でスクリプトにコメントしてください
一定時間が経過したら生成する、という処理は、どのようなロジックを組み込めば実装できるか、ゆっくりと考えて処理に起こしていってください。


2.EnemyGenerator スクリプトを作成する


 設計で考えたことをコメントにて記述し、それから処理を書き始めてみてください。

 大切なことは、1つずつ、処理を分けて考えること、そして、一度にすべての処理をまとめて書く必要はありません
例えば、まずはエネミーのプレファブを生成する処理だけを考えて実装してみてください。
Start メソッドに処理を記述すれば、ゲームの実行時にエネミーが生成されるか確認できます。

 変数は、わかりやすい名前を心がけてくださいHeader 属性を利用したり、コメントを書いておくことが大切です。
常日頃そのように処理を記述しておくと良い癖付けになり、自然と処理も読み書き出来るようになります。

 そうやって1つずつ、1つずつ、処理を積み重ねて、最終目標である、一定時間が経過したらエネミーを生成する、というサイクルを形成してください


EnemyGenerator.cs

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


 スクリプトを作成したセーブします。


3.EnemyGenerator スクリプトを EnemyGenerator ゲームオブジェクトにアタッチし、設定を行う


 作成した EnemyGenerator スクリプトを、ヒエラルキーにある EnemyGenerator ゲームオブジェクトにドラッグアンドドロップしてアタッチしてください。
アタッチしたら、必ずそのゲームオブジェクトを選択してインスペクターから、スクリプトがアタッチされているかを確認します。


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




 インスペクターには、public 修飾子や SerializeField属性をつけた変数の情報が表示されますので、順番に設定してください。

 エネミーのプレファブは、Prefabs フォルダにある EnemySet ゲームオブジェクトを利用します。

 生成までの準備時間(待機時間)は、デバッグ用に短めに設定しておくと、自動生成の確認が行いやすいです。
1.5 〜 3 秒くらいに設定しておいて、また調整を行ってください。


EnemyGenerator ゲームオブジェクト 設定後のインスペクター画像



 設定が完了したら、Unity をセーブして、ゲームを実行して処理を確認しましょう。


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


 ゲームを実行して、一定時間が経過したら(準備時間を超えたら)、エネミーが生成されれば制御成功です。
その処理がずっと繰り返されれば自動生成処理が完成です。


<実行動画 同じ位置からエネミーが一定時間ごとに生成される>
動画ファイルへのリンク

 
 自動生成処理が実装できました。
いまは同じ位置から移動を開始してしまうため、この部分を、エネミーが生成されたあとに移動を開始する位置を変更するようにします。


38.EnemyGenerator スクリプトと EnemyController スクリプトを修正し、エネミーの移動開始位置を X 軸だけランダムに変更し、左右の異なる位置から移動開始するように制御する

1.設計


 設計方法としては、エネミーが生成される位置そのものを毎回変更する方法、エネミーが生成された後に位置を変更する方法があります。
どちらで実装を行っても問題ありません。

 今回の設計は、エネミーが生成された後に、エネミーの位置を変更する方法で考えます。
このとき、生成を行ったスクリプトで位置を変更するのではなく、エネミー側で、位置の変更を行うように設計します。

 1.EnemyGenerator スクリプトでエネミーを生成する
 2.生成されたエネミーには、EnemyController スクリプトがアタッチされているので
 3.生成されたエネミーを GameObject 型の変数に代入するように変更し
 4.この変数を通じて、EnemyController スクリプトの情報を取得して
 5.EnemyController スクリプト内でエネミーの位置を変更する
 6.EnemyController スクリプトの Start メソッドを public 修飾子を持つ別のメソッドに書き換えて、5の処理を行う

 処理の流れは上記のようになります。

 Instantiate メソッドには戻り値があります。
戻り値を持つメソッドに対して左辺に同じ型の変数を用意しておくと、戻り値の情報を代入することが出来ます。
この機能を利用しましょう。

 Instantiate メソッドの場合、処理が実行されると第1引数に利用した型と同じ型の情報が戻り値として提供されます
第1引数に指定している情報の型が GameObject 型であるなら、Instantiate メソッドの左辺に、GameObject 型の変数を用意しておくと、
クローンしたエネミーの情報が左辺の変数に自動的に代入されます。

  GameObject obj = Instantiate(objPrefab, transform, false);
 型で見ると(GameObject) =  (GameObject)

 この手順や代入処理についてはすでに何回も学習しています(ブロック崩し、戦車のゲームなど)ので、ここでしっかりと処理の内容を復習をしましょう。
特に左辺 = 右辺の代入処理については、双方が同じ型でないと代入処理できませんので、常に変数の型を考えておく必要があります。
 


 Instantiate メソッドによって、変数にエネミーのゲームオブジェクトの情報が代入されることにより、
その変数を通じて GetComponent メソッドの処理が実行できます。変数の値(中身)を理解しておくことが重要です。

 GetComponent メソッドによってエネミーのゲームオブジェクトにアタッチされている EnemyController スクリプトを取得します。
こうすることによって、EnemyController スクリプトにある public な情報(public 変数)、public メソッドを実行できます。



 EnemyController スクリプトには public 修飾子を持つメソッドはありませんので、
今回の位置の変更を行う処理を加えて、Start メソッドを別のメソッドに書き換えましょう。
新しくメソッドを用意してもよいですが、Start メソッドの処理も位置の変更と一緒に実行したいため、
メソッドの処理をそのままに、外部のスクリプトから呼び出せるように作り変えます。

 public 修飾子を持つメソッドに変更することで、先ほど EnemyGenerator 側で取得した EnemyController スクリプトから処理を実行出来るようになります。


2.EnemyController スクリプトを修正し、Start メソッドの処理を書き換える


 設計に基づいて、Start メソッドを、public 修飾子を持つ SetUpEnemy メソッドと、名前を変えましょう。
処理については位置の変更処理を追加し、あとはそのままにしておきます。

 このような設計にすることによって、Start メソッドに頼るのではなく、設計で考えたプログラムで、処理を指定した順番通りに動かしていくことが出来ます。


EnemyController.cs


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


3.EnemyGenerator スクリプトを修正し、EnemyController スクリプトに用意したメソッドを呼び出す処理を追加する


 設計に基づいて、GenerateEnemy メソッドの中の処理を書き換えていきます。
日本語のコメントを書いてから、処理のロジックを考えて記述してみてください。


EnemyGenerator.cs

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




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


 スクリプトの修正が終了したら、処理を読み返してみてください
どの処理が、どんな命令になっていてどの部分がつながっているのか、しっかりと把握しておくことが大切です。

 EnemyGenerator スクリプトの GenerateEnemy メソッドで生成されたエネミーのゲームオブジェクト(プレファブのクローン)に対して、
EnemyController スクリプトの取得命令を行い、取得した EnemyController スクリプトの SetUpEnemy メソッドを実行しています。
これによって、SetUpEnemy メソッド内に新しく追記したエネミーの X軸 の位置情報をランダムに設定する処理が実行されるようになり、
エネミーが左右の異なる位置から移動を開始するようなっていれば制御成功です。


<実行動画 左右のランダムな位置からエネミーが一定時間ごとに生成される>
動画ファイルへのリンク


 今後は、スクリプトから、別のスクリプトに用意したメソッドを実行する、という処理が増えていきます。
public 修飾子を持つメソッドのみ、別のスクリプトから呼び出す命令を実行することが出来ます。


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

 次は 手順19 −ゲーム終了判定の実装− です。


5.<応用課題 ー複数行の処理を1行にまとめて記述する方法ー>


 しっかりと処理が読み解けるようになったら、EnemyGenerator スクリプトの GenerateEnemy メソッドの処理を1行の連続した処理で記述する方法を学習します。

    /// <summary>
    /// エネミーの生成
    /// </summary>
    private void GenerateEnemy() {

        // プレファブからエネミーのクローンを生成する。生成位置は EnemyGenerator の位置
        GameObject enemySetObj = Instantiate(enemySetPrefab, transform, false);    //  <=  左辺に GameObject 型の変数を用意して、インスタンスされたエネミーの情報を戻り値で受け取る

    // エネミーのゲームオブジェクトにアタッチされている EnemyController スクリプトの情報を取得して変数に代入
        EnemyController enemyController = enemySetObj.GetComponent<EnemyController>();

        // EnemyController スクリプトの SetUpEnemy メソッドを実行する => Start メソッドの代わりになる処理
        enemyController.SetUpEnemy();
    }

この3行の処理を、1行で記述してみましょう

    /// <summary>
    /// エネミーの生成
    /// </summary>
    private void GenerateEnemy() {

        // プレファブからエネミーのクローンを生成する。生成位置は EnemyGenerator の位置
        // エネミーのゲームオブジェクトにアタッチされている EnemyController スクリプトの情報を取得して変数に代入
        // EnemyController スクリプトの SetUpEnemy メソッドを実行する => Start メソッドの代わりになる処理
        Instantiate(enemySetPrefab, transform, false).GetComponent<EnemyController>().SetUpEnemy();
    }

 以前も学習した、ピリオドの位置によって処理が連続でつながっていく処理です。
ピリオドの前後の型をしっかりと把握しておくことで、3行であった処理が1つ順番に処理されて、メソッドの実行までを1行で記述することが出来るようになります。

 ただし、すべての処理を常にまとめて書け、という意味ではありません。

 この処理の利点は1行でまとめて書けている部分ともう1つ、変数を用意せずに処理が行えている部分です。
逆に考えると、変数を用意しておいて、何回も利用する、という処理には適さない、ということになります。

 今回の場合、1行ずつ変数に必要な情報を取得して、その変数を利用して次の処理を行う、という手順になっていますが、
用意している変数は、その行と次の行で利用する以外では利用されていません。
このような場合には、1行に処理をまとめて書いても問題はありません。利用目的が他にないことが明白だからです。

 ですが、同じ情報を何回も利用する可能性のある処理については、このような命令文では対応できません

 多様な処理の書き方を覚えておくことによって、その都度、最適と思われる処理を記述する、ということが処理の書き方を覚える最大の目標です

 1つの書式にとらわれず、色々な処理の書き方を覚えておくことは、ロジックを組み立てる際に大いに役立ちます。
こういった書式もそのための1つの手法である、と考えておきましょう。

コメントをかく


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

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

Menu



プログラムの基礎学習

コード練習

技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

3D脱出ゲーム(抜粋)

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

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

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

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

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

VideoPlayer イベント連動の実装例

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

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

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

private



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

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