Unityに関連する記事です

 前回の手順で作成したキャラ配置用のポップアップを生成し、非表示にする処理を実装します。
タイルマップをタップした際に、キャラの生成ではなく、代わりにポップアップを表示する制御に変更をします。

 この手順では、ポップアップの生成と、ポップアップ表示/非表示の制御の実装を行うことが目的です。


<実装動画>
動画ファイルへのリンク


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

手順19 ーPlacementCharaSelectPopUp ゲームオブジェクトの生成処理の実装ー
33.PlacementCharaSelectPopUp スクリプトを作成する
34.CharaGenerator スクリプトと GameManager スクリプトを修正する



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

 ・Button.onClick.AddListenerメソッド
  ・Selectable.interactable 変数
 ・DOTweenの補間機能と実装例 ーOnComplete メソッドー
 ・Start メソッドを使わない初期設定の手法
 ・Start メソッドに依存しない処理の組み立て方



33.PlacementCharaSelectPopUp スクリプトを作成する

1.設計


 まずはポップアップ自体の生成処理から実装します。
こちらの処理が無事に実装できてから、次の手順でポップアップ内部に生成するボタンの作成と生成処理が実装できるようになります。

 慣れてきたらこれらの手順をまとめて実装しても構いませんが、その場合にも設計をしっかりと考えた上で実装していくようにしましょう。
スクリプトを記述するのは最後の段階になります。つまり、設計上で問題がなく完成させることがわかってから記述していくことになります。


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


 ポップアップのゲームオブジェクトを制御するためのスクリプトになります。
まずは表示/非表示の制御を実装し、次の手順で、ポップアップ内の各コンポーネントの制御を実装します。

 表示/非表示に際してはボタン操作を制御する必要があるため、その情報を変数として登録し、制御できる状態にします。
このとき、変数名とアサインするゲームオブジェクトの名称を同じにしておくと分かりやすくなります。

 また TODO を活用し、次回以降に実装するものや、設計する際に検討しておくべき部分を記述しています。
自分でスクリプトを作成する際にも、ロジックを考えるときに役立ちますので、TODO のコメントを活用していきましょう。

 なお、次の手順で処理を追加しないとエラーになる処理がありますので、その部分はコメントアウトをしてあります。
後程、処理を追加してからコメントアウトを解除してください。


PlacementCharaSelectPopUp.cs


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


3.<Button.onClick.AddListener メソッド>


 Button コンポーネントには OnClick というイベントを登録する場所があります。ここにゲームオブジェクトのアサインを行うことで、
アサインしたゲームオブジェクトにアタッチされているスクリプトに宣言されている public 修飾子のメソッドを登録することができます。


インスペクター画像



 この処理をスクリプトから制御して追加して行う処理が、onClick.AddListenerメソッドです。引数のない場合と引数のある場合で書式が変化します。

 利点は、private 修飾子のメソッドでも登録できること、スクリプトから登録しているので、Unity内の上記の画像のButton部分への登録作業や、確認をする必要がなくなること、などがあります。

 今回は引数のないメソッドを登録していますので、引数にはそのままメソッド名を記述します。メソッドの()を書くとエラーになるので、メソッド名のみを記述します。

 // 各ボタンにメソッドを登録
  btnChooseChara.onClick.AddListener(OnClickSubmitChooseChara);

 btnClosePopUp.onClick.AddListener(OnClickClosePopUp);
 
 コメントにもあるように、この処理はボタンにメソッドを登録するだけですので、メソッド自体の実行処理ではありません。
対象となるボタンを押したときに実行されるメソッドを登録している処理になります。

 詳細については公式のリファレンスを参考にしてください。またネットには記事が多くありますので調べてみましょう。

参考サイト

Unity公式スクリプトリファレンス
Button.on.Click.AddListener
https://docs.unity3d.com/ja/2018.4/ScriptReference...


4.<Selectable.interactable 変数>


 Button コンポーネントは、bool 型の interactable プロパティを持って(管理して)います。

 こちらのスイッチがオン(true)の場合、ボタンは有効な状態です。これを活性状態といいます。

 こちらのスイッチがオフ(false)の場合、ボタンは無効な状態です。
ボタンの色が Disabled Color で設定されている色に変わります(デフォルトは半透明な灰色)。これを非活性状態といいます。

 この情報はプロパティですので、スクリプトから操作をすることが出来ます。
今回はこちらの機能を利用してボタンを制御しつつ、視覚的にもわかるようにしています。

 実際にインスペクターで操作をしてみて、どのように変化するかを確認しておきましょう。

<ボタンを押せない状態にする処理>
  // ボタンを非活性化(半透明で押せない状態)
 btnChooseChara.interactable = false;
        
 btnClosePopUp.interactable = false;

<ボタンを押せる状態にする処理>
  // ボタンを非活性化(半透明で押せない状態)
 btnChooseChara.interactable = true;
        
 btnClosePopUp.interactable = true;

 今回はこの活性化/非活性化の切り替え処理をメソッドの引数を利用して1つの処理で運用出来るようにしています。
このような設計にしておくことで、false 用、true 用といった処理を分けて記述する必要がなくなるためです。
引数の活用方法を覚えると設計が楽になるだけではなく、面白くなります。

    /// <summary>
    /// 各ボタンのアクティブ状態の切り替え
    /// </summary>
    /// <param name="isSwitch"></param>
    public void SwithcActivateButtons(bool isSwitch) 
        btnChooseChara.interactable = isSwitch;
        btnClosePopUp.interactable = isSwitch;
    }

 この情報もインスペクター上で確認を行うことで出来ます。正常に制御が行われているかを確認しておいてください。





参考サイト
Unity公式スクリプティングAPI
Selectable.interactable
https://docs.unity3d.com/2019.1/Documentation/Scri...
すくまりのメモ帳 様
【Unity】Buttonをinteractableで無効化したり、動的にイベントを追加する方法
https://squmarigames.com/2018/12/10/unity-beginner...


5.<DOTweenの補間機能と実装例 ーOnComplete メソッドー>


 OnComplete メソッドも SetLoops メソッドや SetEase メソッドと同じように、メインとなる他の DOTween メソッドに付随する処理です。

 この処理はコールバック処理になっており、一緒に処理しているメインのメソッドの処理の終了を待ちます
その処理が終了してから、この OnComplete メソッドの中に記述した処理を実行してくれます。

 今回は DOFade メソッドによって CanvasGroup コンポーネントの Alpha の値が 0 に変更された後に、この OnCompleteメソッドの処理が実行されるようになっています。
OnComplete メソッドには CharaGenerator クラスの InactivatePlacementCharaSelectPopUp メソッド処理が記述されていますので、この処理は、DOFade メソッドの処理が終わるのを待った後に自動的に実行されます。

 // ポップアップの非表示
 canvasGroup.DOFade(0, 0.5f).OnComplete(() => charaGenerator.InactivatePlacementCharaSelectPopUp());


 1行でも書くことは出来ますが、長くなると読みにくくなってしまうため、可読性を高めるためにも複数行に渡って処理を書いてもいいでしょう。
こういった配慮もスクリプトを記述する際には必要ですので、可読性の高い処理を書けるように心がけてください。


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


 アタッチすると複数の変数がインスペクターで設定するようになっていますので、それぞれに情報をアサインしてください。


インスペクター画像



 以上で設定は完了です。


7.PlacementCharaSelectPopUp ゲームオブジェクトをプレファブにする


 完成した PlacementCharaSelectPopUp ゲームオブジェクトを Prefabs フォルダへドラッグアンドドロップしてプレファブにします。
その後、ヒエラルキーにある PlacementCharaSelectPopUp ゲームオブジェクトは次の手順で利用しますので、
そのまま削除してないで、一旦、PlacementCharaSelectPopUp ゲームオブジェクトを非表示の状態にしておいてください。

 以上でこの手順は完成です。


34.CharaGenerator スクリプトと GameManager スクリプトを修正する

1.設計


 CharaGenerator スクリプト内でポップアップの生成と設定の制御を行うようにします。
このとき、このスクリプトを処理の起点としてしまうと、修正する際や、処理の順番を追いかけることが難しくなります。

 そのため、EnemyGenerator スクリプトと GameManager スクリプトのような関連性でこの CharaGenerator スクリプトの処理も制御を行うように設計を考えてみます。

<処理のフローチャート>
 GameManager スクリプトの Start メソッドを起点として、CharaGenerator スクリプトの設定処理を実行する
          ↓
 CharaGenerator スクリプトの SetUpCharaGenerator メソッドをコルーチンで実行し、その中でポップアップの生成処理を実行する
          ↓
 CharaGenerator スクリプトの CreatePlacementCharaSelectPopUp メソッドをコルーチンで実行し、ポップアップの生成と、PlacementCharaSelectPopUp スクリプトの設定処理を実行する
   この処理が終了するまで処理を一時中断する。これは後で作成するキャラのボタンなどを作成する処理が完成するのを待つため
          ↓
 PlacementCharaSelectPopUp スクリプトの SetUpPlacementCharaSelectPopUp メソッドを実行し、ポップアップの設定を行う
   後々にはここでキャラのボタンの生成処理も行う

 この設計も、EnemyGenerator スクリプトの制御と同じように、GameManager スクリプトの Start メソッドから処理が順番に実行されていく形式になっています。

 実際にスクリプトの修正や作成を行う場合には、最後の処理から製作を始めて起点となる部分まで戻る用にすると処理が書きやすくなります。

 スクリプトの処理は呼び出し命令と呼び出されるメソッドとが1対になっているため、先に呼び出し命令を書いてしまうと、呼び出されるメソッドがないため、エラーを表示します。
そのため。呼び出されるメソッド(処理の実体)を先に作成してから、呼び出し命令を記述するようにする方がいいでしょう。
 
 その観点で処理を考えると、処理の起点ではなく、目標地点(最終的な処理)からさかのぼっていくことで、
呼び出されるメソッドを書く → 呼び出し命令を書く、という順番で処理の実装ができるためです。


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


 GameManager スクリプトの Start メソッドから実行するための設定用の処理や、ポップアップの生成処理などを実装します。
変数の宣言は、ポップアップの生成用のプレファブや生成地点の情報などを追加します。

 Update メソッド内においてタイルマップをタップした際にキャラを生成する処理を実装していますので、
こちらを代わりにポップアップを表示する処理に変更します。

 ポップアップについてですが、タップするたびに新しく生成をし直すことも出来ます。
ですが今回のケースの場合、ポップアップの内容に変化はないため、最初に1回だけ生成しておいて、あとは表示/非表示を切り替える制御で実装を行っています。


CharaGenerator.cs

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


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


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


 CharaGenerator ゲームオブジェクトを選択し、インスペクターを確認します。
新しく宣言した変数が2つ、インスペクターに追加されていますのでアサインします。

 PlacementCharaSelectPopUpPrefab 変数には Prefabs フォルダにある PlacementCharaSelectPopUp ゲームオブジェクトをドラッグアンドドロップしてアサインします。
自動的に PlacementCharaSelectPopUp スクリプトが登録(代入)されます。

 CanvasTran 変数にはヒエラルキーにある Canvas ゲームオブジェクトをドラッグアンドドロップしてアサインしてください。
自動的に Transform コンポーネントが登録(代入)されます。


インスペクター画像


 以上で設定は完了です。



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


 このスクリプトが処理の起点となるように、Start メソッドに CharaGenerator スクリプトの設定を行う処理を追加します。
その際に CharaGenerator スクリプトを制御できるように変数を追加しています。


GameManager.cs


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


5.<Start メソッドを使わない初期設定の手法>


 Start メソッドはゲームを実行したり、あるいは Instantiate メソッドによって生成されたりしたときに1回だけ自動的に動くメソッドです。
その特性上、クラス内部の情報の初期設定を行う用途に利用されるケースが多いです。

 ですがコンストラクタメソッドとは異なり、引数を持たないため、例えば外部のクラスによって Instantiate メソッドにより生成されたとしても
Start メソッドでは、その外部のクラスから引数を通じて情報をもらうということが出来ません。

 今回実装している CharaGenerator スクリプトにおいては、Start メソッドの代わりとなる SetUpCharaGenerator メソッドを用意し、外部クラスから実行しています。


    private GameManager gameManager;


    public IEnumerator SetUpCharaGenerator(GameManager gameManager)
    {
        this.gameManager = gameManager;

        //TODO ステージのデータを取得

        //TODO キャラのデータをリスト化

        yield return StartCoroutine(CreatePlacementCharaSelectPopUp());
    }

 メソッドの名前の通り、これは Start メソッドの代わりに、引数を持ち、外部クラスから情報を受け取って初期設定を行うためのメソッドです。
手動で動かすコンストラクタメソッドのようなイメージです。

 外部クラスより引数として GameManager 型の情報を gameManager 変数として受け取っています。
そしてそれを、private で用意している、このクラスにある gameManager 変数に代入しています。

 this.gameManager = gameManager;

 左辺と右辺の変数の gameManager 変数は、同名の変数ではありますが、スコープが異なります
ここを理解しておくことが重要です。



 左辺の gameManager 変数は、頭に this がついています。
これは、this (このクラス) にて宣言されている gameManager 変数を指しています
つまり、宣言フィールドにて宣言されている private な gameManager 変数です。

    private GameManager gameManager;



 右辺の gameManager 変数は、頭には this がありません。
よって、この変数はこのメソッドの引数で宣言されている方の gameManager 変数を指しています。

  public IEnumerator SetUpCharaGenerator(GameManager gameManager) // ← これ

 このように同じ gameManager 変数ですが、スコープの異なる変数です。
this の有無で、プログラム側が自動的に判断します。

 名前に惑わされないようにしていきましょう。特に、マウスクリックで変数を調べてみると可視化できるので理解が深まると思います。



 このメソッドの処理のポイントは、変数への代入処理にあります。つまり、初期化の処理です。

 private 修飾子で宣言されている gameManager 変数(this の方) は、そのままでは null の状態です。
クラスは参照型に分類される型であるため、値型の型とは違って、宣言しても中身には何も入っていません。null です。
また private であるため、そのままではインスペクターよりアサインもできないので、
ゲーム開始時に、この変数は null のままスタートします。そのまま使おうとすると null エラーが出ます。

 こういった、private の変数に必要な情報に代入したいケースに対応するため、
SerializeField属性を付与してアサインさせたり、GetComponent メソッドで取得する以外に、
今回ように、外部のクラスから実行させるメソッドを用意し、その引数を経由して取得する方法があります。



 今回の実装では、外部のクラス(ここでは GameManager スクリプト)から実行できる pulbic のメソッド(本教材の場合、Setup 〜 で統一)を採用し、
外部のクラスから GameManager クラスの情報を、メソッドの引数を通じてもらうようにプログラムを組んでいます。

 そうすることで、このクラスが自分で GameManager クラスの情報を探しに行ったり、GetComponent メソッドを使わなくても、
メソッドの引数を通じて、必要な受け取ることで private な変数に情報を代入することができる仕組みです。

 当然、代入処理だけで済んでいるので、ゲームオブジェクトを探したり、コンポーネントを取得する処理もないので、処理の負荷も軽くなります
 
 他のクラスにも Setup 〜 で始まる処理が書いてあると思いますが、処理の内容としては同じ挙動です。



 まとめると、クラス内にある変数に代入処理を行う場合、クラス内で必要な情報を直接探しに行くという手法以外に、
メソッドの引数を通じて、外部のクラスから必要な情報を送ってもらって、それを受け取って変数に代入させる手法があり、
後者の方が効率がよい処理になっている、ということになります。

 先ほども説明したように、初期化用のメソッドの意味を持っているので、コンストラクタメソッドに近い動きをするイメージです。
そのため、これらの Setup 〜 メソッドを用意しているクラスには Start メソッドがありません


6.<Start メソッドに依存しない処理の組み立て方>


 Start メソッドは処理を行う順番が Unity 任せになってしまうため、
複数のスクリプトにおいて Start メソッドが書かれている場合、
処理の順番が前後してしまったせいで、処理が間に合わなくてエラーが出てしまう可能性が残ります。

 代わりに先ほどのような初期化専用のメソッドを用意しておくことで、
プログラムを書く側(私たち)が処理を実行するタイミングを組み立てていくことができる、というメリットがあります。
Unity が勝手に動かさない代わりに、こちらで書いた順番通りに動かすことができる、という意味です。



 これも、身近なものに置き換えて考えてみるとわかりやすいです。

 誰かに「ご飯の用意して」「買い物行ってきて」「お風呂掃除して」というお願いをされたとき、
特に、順番がなかったら自分の都合で動きます。これが Unity に任せた場合です。
Unity の都合で、各スクリプトにある Start メソッドが順不同で実行されます。
そのため、「ご飯の用意」が最初のこともあれば、「お風呂掃除」が最初のこともあり、処理の順番が毎回不確定です。

 ですが、最初から「買い物行ってきて」⇒「お風呂掃除して」=>「ご飯の用意して」とお願いされれば、
問題があれば相談すれば順番を変えれますし、基本的には順番を守ります。
これが、プログラムでいうところの、処理を実行するタイミングを組み立てて、順番通りに動かす、という場合です。

 そしてプログラムの場合には、後者の方がプログラムを書きやすくなりますね。
処理の順番も変更できて、書いた通りに実行してくれる訳ですし、問題があった場合にもどこまで動いているのか把握できるので、原因を特定しやすくなります。

 この辺りに注目して、どのようにして処理が組み立てられているのかを読み解いていっていただけるとよいと思います。


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


 GameManager ゲームオブジェクトを選択し、インスペクターを確認します。
新しく宣言した変数がインスペクターに追加されていますので、ヒエラルキーにある CharaGenerator ゲームオブジェクトをドラッグアンドドロップしてアサインしてください。
自動的に CharaGenerator スクリプトが登録(代入)されます。


インスペクター画像


 以上で設定は完了です。


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


 ポップアップが生成されて、その後自動的にポップアップが非表示なるか確認します。
忘れずに、PlacementCharaSelectPopUp スクリプト内のコメントアウトしていた処理を修正し、コメントアウトを解除してください


ヒエラルキー画像



 今までとおなじようにタイルマップ上をタップしてみてください。
キャラの生成処理ではなくて、非表示になっていたポップアップが徐々に表示されれば制御成功です。

 両方のボタンをそれぞれ押して、ポップアップが閉じるか確認を行います。
何回か押してみて、複数のポップアップが作られていないかも確認しておいてください。


<実装動画>
動画ファイルへのリンク




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

 次は 手順20 −キャラ選択用のボタン型ゲームオブジェクトの作成− です。

コメントをかく


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

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

Menu



技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

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

3D脱出ゲーム(抜粋)

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

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

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

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

VideoPlayer イベント連動の実装例

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

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

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

private



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

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