i-school - 2DタイルマップRPG 手順28
 会話ウインドウの表示位置をキャラの上下位置ではなく、Canvas の固定位置に表示する制御処理を実装します。
 
 処理の実装内容が多いので、2回に分けて実装を行います。

 まずは最初に固定型の会話ウインドウの作成と、NPC キャラに話しかけた際に、NonPlayerCharacter スクリプトの設定に応じて
今まで通りキャラの位置に会話ウインドウを表示する処理と、固定の会話ウインドウに表示する処理を自動で切り替える機能を実装します。


<実装動画 従来通り ーNonPlayerCharacter スクリプトの isFixedTalkWindowUsing 変数がオフ(false)ー>
動画ファイルへのリンク


<実装動画 固定型 ーNonPlayerCharacter スクリプトの isFixedTalkWindowUsing 変数がオン(true)ー>
動画ファイルへのリンク


手順28 ー固定型の会話ウインドウの表示の準備ー
49.Canvas ゲームオブジェクトを作成し、固定型の会話ウインドウを表示するためのゲームオブジェクト群を作成する
50.UIManager スクリプトを作成し、NonPlayerCharacter スクリプトを修正して、固定型の会話ウインドウの表示・非表示の制御を追加する



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

 ・プレファブの活用
 ・自分で処理の分岐を考えてロジックを組み立てる



49.Canvas ゲームオブジェクトを作成し、固定型の会話ウインドウを表示するためのゲームオブジェクト群を作成する

1.設計


 この手順では最初に固定型の会話ウインドウの作成と、NPC キャラに話しかけた際に、NonPlayerCharacter スクリプトの設定に応じて
今まで通りキャラの位置に会話ウインドウを表示する処理と、固定の会話ウインドウに表示する処理を自動で切り替える機能を実装します。

 この機能が問題なく実装できたら、次の手順において、各 NPC キャラごとに会話ウインドウの設定を行う方式ではなく、
1つの設定をすべての NPC のキャラに設定することで会話ウインドウの設定を片方に統一する制御処理を実装します。

 これはキャラ数が増えてきたときに有効な制御です。例えば 50 体の NPC がいる場合に、それらすべてを1つずつ設定していたので埒が明きません。
そのようなことをしていては時間がいくらあっても足りなくなります。

 作業効率を上げるためにはそういった手作業の部分をプログラムを利用することによって簡便化していくことが出来ますので、そういった考え方と実装方法を学習します。


2.Canvas ゲームオブジェクトを作成し、DialogCanvas ゲームオブジェクトを子オブジェクトとして作成する


 最初に画面に表示する固定型の会話ウインドウの作成を行います。

 これは NPC キャラの場合と異なり、毎回固定の位置に、最前面で表示を行えばよいため、新しい Canvas ゲームオブジェクトを作成して、
その子として DialogCanvas ゲームオブジェクトのプレファブを設置することで作成します。

 プレファブを上手に活用していくことも作業効率を上げる1つの方法になります。



 ヒエラルキーの空いている部分で右クリックをしてメニューを開き、UI => Canvas を選択します。
名前はそのままでもよいですし、わかりやすい名前に変えても構いません、自分が作業しやすく、第三者が見てもわかることを心掛けておいてください。

 新しく作成した Canvas ゲームオブジェクトの上に、Prefabs フォルダにある DialogCanvas ゲームオブジェクトをドラッグアンドドロップして子オブジェクトにします。
プレファブの状態は解除してもしなくても構いません。


ヒエラルキー画像



インスペクター画像



Sceneビューと Gameビュー画像



 親子関係になっていることを確認しましょう。
続いて、DialogCanvas ゲームオブジェクトの設定を行います。


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


 DialogCanvas ゲームオブジェクトの設定を変更します。

 名前を TalkWindow のように、わかりやすい名前に変更しても構いません。

 次に、DialogCanvas ゲームオブジェクトにアタッチされている Canvas コンポーネントと Canvas Scaler コンポーネント、
Graphic Raycaster コンポーネントの3つのコンポーネントを Remove してください。

 これは親オブジェクトである Canvas ゲームオブジェクトに同じコンポーネントがアタッチされているため、このゲームオブジェクトでは今回は不要になっているためです。



 続いて、位置を変更します。画面の下部に位置するように設置してください。

 それ以外は自由に設定してください。ウインドウのサイズ、文字のサイズ、なども任意です。

 以下に参考例を記載しておきます。この通りである必要はありません。


<TalkWindow ゲームオブジェクト>

 名前を変えていなければ DialogCanvas ゲームオブジェクトになります。

 各子オブジェクトをまとめるフォルダ役です。また、DialogController スクリプトがアタッチされているので
各子オブジェクトの制御を行っているゲームオブジェクトにもなります。なお、各子オブジェクトの役割は同じです。


インスペクター画像



Sceneビュー画像




<imgFrame ゲームオブジェクト>

 メッセージウインドウのフレームの画像を設定しています。


インスペクター画像



Sceneビュー画像




<txtDialog ゲームオブジェクト>

 メッセージを表示する部分です。
フレーム役の imgFrame ゲームオブジェクトよりも小さいサイズにしておくとフレーム内に文字が収まります。
実際にどの辺りに、どの大きさで文字を表示したいのかを考えて設定してください。


インスペクター画像



Sceneビュー画像




<txtTitleName ゲームオブジェクト>

 会話している対象の名称が表示されます。
今回はフレーム役のゲームオブジェクトは削除していますが、利用しても構いません。
どのように画面上に表示したいのか、自分のイメージにあったものを採用してください。


インスペクター画像



Sceneビュー画像



 以上で固定型の会話ウインドウは完成です。


50.UIManager スクリプトを作成し、NonPlayerCharacter スクリプトを修正して、固定型の会話ウインドウの表示・非表示の制御を追加する

1.設計


 会話ウインドウを2種類用意することができました。
この手順では、どうすればこれらのウインドウの表示制御を自動的に分岐するようになるのか設計を考えていきます。

 実装に当たって最も簡単な方法としては、会話ウインドウを切り替えるための変数を用意しておきます。
この値に応じて、会話ウインドウを稼働型(今までのようにキャラの上下に表示)か、固定型(Canvas に用意した同じ位置に表示)とで切り替えるような処理を記述します。

 この切り替えるための変数は public 修飾子にしておくことにより、インスペクターより変更することができますので、
インスペクターから設定を行うことによって、キャラの上下タイプにするか、固定タイプにするかの切り替えができるようにもしておきます。



 現在、NPC のキャラの会話ウインドウは DialogCanvas ゲームオブジェクトを制御することによって処理されています。
この DialogCanvas ゲームオブジェクトにアタッチされている DialogController スクリプトが実際にはそれらの制御を行っています。

 この DialogController スクリプトに対して、DialogCanvas ゲームオブジェクトの表示・非表示の命令を出しているスクリプトがあります。
つまり、そのスクリプト内に、先ほどの変数と、それを利用した分岐を用意することができれば、自動的に会話ウインドウの種類を切り替える処理を実装することが出来ます。

 今回作成した固定型の会話ウインドウには DialogController スクリプトがアタッチされていますが、このスクリプトに対して命令を出す対象者がまだいません。
これはどういう状態なのか、処理の命令体系を考えてみましょう。

<現在の会話ウインドウの命令体系>
 1.NonPlayerCharacter スクリプトは、自分の子オブジェクトである DialogCanvas ゲームオブジェクトにアタッチされている DialogController スクリプトに命令を出している
 2.各 NonPlayerCharacter スクリプトに対して、1対1 の関係で会話ウインドウがある(DialogController スクリプトがある)

<固定の会話ウインドウの命令体系>
 1.NonPlayerCharacter スクリプトから固定型の会話ウインドウに対して命令を出したい
 2.DialogCanvas ゲームオブジェクトと異なり、各 NPC ごとに存在していないため、1対1 での関係性ではない。
   そのため、1対多数 の構図になるため、各 NonPlayerCharacter からの命令を一括で管理して、固定の会話ウインドウの表示の制御を行う「仲介者」となるスクリプトが必要
 3.各 NonPlayerCharacter スクリプトは「仲介者」のスクリプトに対して、会話ウインドウの表示命令を出す。
   これを受けて「仲介者」のスクリプトが固定の会話ウインドウの表示・非表示の制御を行う

 固定の会話ウインドウの場合、従来のように命令が出せない状態になっている、ということが分かります。
そのため、NonPlayerCharacter スクリプトが命令を出せるようにするための、「仲介者」となるスクリプトを作成していきます。

 それから NonPlayerCharacter スクリプトの修正を行うことで、命令先を、「仲介者」にして固定の会話ウインドウの制御をさせるのか、
自分の子オブジェクトに個別の会話ウインドウの制御をさせるのか、自動的に分岐する処理を作成出来るようになります。


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


 UIManager スクリプトにおいて改めて DialogController スクリプトを同じ処理を書く必要はありません。
このスクリプトでは、NonPlayerCharacter スクリプトより、固定型会話ウインドウ表示・非表示の命令を受けた際に、
固定型の会話ウインドウのゲームオブジェクトである TalkWindow ゲームオブジェクトにアタッチされている
DialogController スクリプトに対して、会話ウインドウの表示・非表示の命令を行っているだけになります。

 会話ウインドウの表示用のメソッドと非表示用のメソッドの内容を確認してみるとわかりますが、
NonPlayerCharacter スクリプトが直接自分の子オブジェクトの DialogCanvas ゲームオブジェクトの DialogController スクリプトに命令していたように、
UIManager スクリプトは、TalkWindow ゲームオブジェクトにアタッチされている DialogController スクリプトに命令をおこなっているため、
呼び出している処理は、どちらもの同じ内容になっています。

 これが今回、DialogCanvas ゲームオブジェクトのプレファブを利用して、TalkWindow ゲームオブジェクトを作成した一番の理由であり、
「仲介者」という役割そのものになります。 

 DialogCanvas ゲームオブジェクトと同じ設計のゲームオブジェクトで出来ているため、アタッチされている DialogController スクリプトの処理がそのまま転用出来ています。


UIManager.cs

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

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



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


 ヒエラルキーの空いている場所で右クリックをしてメニューを開き、Create Empty を選択します。
新しいゲームオブジェクトが1つ作成されますので、名前を UIManager に変更します。

 インスペクターを確認し、Transform コンポーネントの Position の値をすべて 0 にします。(はじめから 0 なら処理は不要です。)


ヒエラルキー画像


 
 作成した UIManager スクリプトをドラッグアンドドロップしてアタッチします。
アタッチしたあとは必ずゲームオブジェクトのインスペクターを確認して、アタッチされているかを確認してください。
 
 dialogController 変数がインスペクター上に表示されていますので、
DialogController スクリプトがアタッチされている TalkWindow ゲームオブジェクトをヒエラルキーからドラッグアンドドロップしてアサインしてください。


インスペクター画像



 これにより、UIManager スクリプトから、固定型会話ウインドウ用のゲームオブジェクトである TalkWindow ゲームオブジェクトの
DialogController スクリプトに対して命令を出せる状態になりました。

 この関係性は、NonPlayerCharacter スクリプトが DialogCanvas ゲームオブジェクトの DialogController スクリプトに対して命令を出しているのとまったく同じ状態になります。


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

 
 最後に、NonPlayerCharacter スクリプトを修正して、いままでの稼働型の会話ウインドウではなく、
固定型の会話ウインドウに対してメッセージを表示する制御を追加します。

 設計としては、最初に説明させていただいたように、会話ウインドウの設定を切り替えるための変数を public 修飾子で宣言して用意しておいて、
そちらを切り替えることによって、稼働型の会話ウインドウか、固定型の会話ウインドウかを切り替え制御するようにします。

 分岐を作成する場所のイメージがわいているのであれば、この変数を利用して分岐を作成してみてください
それから教材をみていただくといいでしょう。いままで学習してきている技術だけで作成することができますので、挑戦してみてください。



 固定型の会話ウインドウに対して命令を用意しているのは UIManager スクリプトになりますので、このスクリプトも扱えるように変数を用意して、
ゲームの開始時に変数へ情報を取得して、変数から UIManager スクリプトを操作できる状態にしておきます。

 なお、変数の宣言を他の変数の途中に追加しているのは、修飾子の種類を合わせておくためです。
スクリプトを記述する際にはなるべく読みやすい設計を心がけましょう。 


NonPlayerCharacter.cs

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


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


5.各 NPC ゲームオブジェクトの設定を行う


 NPC 用のゲームオブジェクトのインスペクターを確認します。
新しく public 修飾子で宣言した isFixedTalkWindowUsing 変数が追加されていますので、チェックを入れて true の状態にします。

 複数ある場合には、チェックをいれたものと、そうでないものを用意しておきましょう。
分岐のチェックがはかどります。


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



 以上で設定は完了です。


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


 すべての実装が終了したら処理の内容を読みかえして、どのような制御を期待しているのか、どの部分をデバッグするのかを
念頭に置いてからゲームを実行して処理の制御を確認していきましょう。

 自分でゲームを製作していく際にはすべて自分で考えて、どの部分を確認していくのかを把握しておく必要があるためです。


<実装動画 従来通り ーNonPlayerCharacter スクリプトの isFixedTalkWindowUsing 変数がオフ(false)ー>
動画ファイルへのリンク


<実装動画 固定型 ーNonPlayerCharacter スクリプトの isFixedTalkWindowUsing 変数がオン(true)ー>
動画ファイルへのリンク


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

 次は 手順29 ー固定型の会話ウインドウと稼働型の会話ウインドウの切り替え制御処理の実装ー です。