i-school - 2DタイルマップRPG 手順16
 引き続き、敵とのランダムエンカウントの発生方法の実装を行います。実際の戦闘シーンはありません。


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


手順16 −ランダムエンカウント後のシーン遷移の実装−
27.Battle シーンから Main シーンへと遷移する処理を実装する
28.Battle シーンから Main シーンへと遷移し、エンカウントした位置にプレイヤーキャラを設置する処理を実装する


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

 ・Button.onClick.AddListenerメソッド
 ・Canvas 内にゲームオブジェクトを自分で考えて配置する



27.Battle シーンから Main シーンへと遷移する処理を実装する

1.設計


 無事に遷移ができましたので、次は、Battle シーンにボタンを設置し、このボタンを押すと Main シーンへと遷移する処理を実装します。

 シーンは読み込まれるたびに破棄されて、新しいシーンが作成されます
そのため、Battle シーンから Main シーンへの遷移は「前の Main シーンへ戻る」という処理ではなくて、「新しい Main シーンへ遷移する」という処理になります。
よって、プレイヤーキャラは初期位置からスタートすることになりますが、それではエンカウントのたびにスタート地点へ戻されてしまって、
ゲームとしては成り立たなくなります。

 まずは最初に、Battle シーンから Main シーンへ再度遷移する処理を実装します。
その処理が実装できたら、Main シーン内にあるプレイヤーキャラの位置と方向をエンカウント時の情報を反映する処理を実装します。

 何度も書いていますが、一度にすべての処理を実装する必要はありません
目標となる処理と目指して、1つずつ、こつこつと実装を積み重ねて作っていくことが求められます


2.Battle シーンに Main シーンへ遷移するためのボタンを設置する


 Battle シーンのヒエラルキーの空いている場所で右クリックをしてメニューを開き、UI => Canvas を選択してください。
Canvas ゲームオブジェクトが作成されますので、この子オブジェクトとして Button コンポーネントを持つゲームオブジェクトを作成してください。
ボタンなどの UI 機能は Canvas ゲームオブジェクト内に作成しないと機能しませんので注意してください。

 このボタンがバトル終了の仮処理を行うボタンになり、押下することで Battle シーンを終了して、Main シーンへと遷移する処理を実行させます。

 配置などは自由です。今までの学習を元にして、Button を作成して配置してみてください。


ヒエラルキー画像 参考



Scene ビューとGame ビュー画像 参考



3.BattleManager スクリプトを作成する


 Battle シーンでのボタン操作を管理するためのクラスを作成します。
今後 Battle シーンを作りこむ際にも利用していただくことも可能です。

 今回は Button コンポーネントを制御するための処理を実装します。

 Button コンポーネントにはインスペクターより OnClick というイベント部分に public 修飾子のメソッドを登録することが出来ますが、
この処理は多くの手順で実装しますので、今回は、スクリプトを使ってこの OnClick イベントにメソッドを登録する処理を実装します。


BattleManager.cs

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


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


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


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


インスペクター画像



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

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

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

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

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

参考サイト

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


5.BattleManager ゲームオブジェクトを作成し、BattleManager スクリプトをアタッチする


 BattleManager スクリプトをアタッチするためのゲームオブジェクトを作成します。

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

 その後、BattleManager スクリプトを BattleManager ゲームオブジェクトにドラッグアンドドロップしてアタッチしてください。
アタッチしたら必ずインスペクターを見て、アタッチが正常に行えているかを確認します。



 BattleManager スクリプトには btnBattleEnd 変数が表示されていますので、こちらに制御したい Button コンポーネントがアタッチされているゲームオブジェクトを
ヒエラルキーよりドラッグアンドドロップしてアサインしてください。自動的にそのゲームオブジェクト内にある Button コンポーネントの情報が登録されます。


インスペクター画像



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


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


 Main シーンからゲームを実行して、エンカウントして Battle シーンへと遷移してください。

 Battle シーンに遷移したら、バトル終了用のボタンを押してください
再度 Main シーンへと遷移が発生すれば制御成功です。


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



 Main シーンに再度シーン遷移したとき、プレイヤーキャラはエンカウントが発生した位置ではなく、スタート地点に配置されます
これは以前の Main シーンに戻っているのではなく、新しい Main シーンを読み込んでいるためです。

 最後に Main シーンへ遷移した際にプレイヤーキャラがエンカウントした位置と方向に配置されるように制御を行います。


28.Battle シーンから Main シーンへと遷移し、エンカウントした位置にプレイヤーキャラを設置する処理を実装する

1.設計


 Battle シーンから Main シーンへの遷移処理も実装できました。
最後に Main シーンへ遷移した際にプレイヤーキャラの位置と方向を、エンカウント時の状態に設定する処理を実装します。

 すべてのゲームオブジェクトはシーンの遷移とともに破棄されます
そのため、ゲームオブジェクトにアタッチされているスクリプト内の変数などの情報も失われてしまいます

 ただし、DontDestroyOnLoad メソッドにて引数で指定されているゲームオブジェクトは、シーンの遷移によっても破棄されないゲームオブジェクトになります
この機能を利用して、シングルトンクラスである GameData クラスに Main シーンでエンカウントしたときのプレイヤーキャラの位置情報と向き情報を保持するようにします。
具体的には、EncountManager クラス内のエンカウント処理の中に、この保持の処理を追加します。TODO 機能が役立つでしょう。

 GameData クラスは DontDestroyOnLoad メソッドが有効であるため、シーン遷移を行っても破壊されないゲームオブジェクトです。
このゲームオブジェクトにアタッチされている GameData クラス内に Main シーンでのプレイヤーキャラの情報を保持しておければ、
その情報を使って、新しい Main シーンを読み込んだ際に、GameData クラスに保持しているエンカウントした位置と方向の情報を使うことで、
以前の Main シーン内のプレイヤーキャラの位置と方向を再現することが出来ます。

 プレイヤーキャラの位置と方向の情報の設定については PlayerController クラスに処理を実装することで、位置と方向を修正することが出来ます。



 処理の流れをまとめてみましょう。

 1.EncountManager クラスの JudgeRandomEncount メソッド内においてエンカウントが発生する
 2.このメソッド内で GameData クラスに新しく用意するメソッドを実行する処理を追加して、プレイヤーキャラの位置と向いている方向の情報を引数で渡す
 3.GameData クラス内にプレイヤーキャラの位置と向いている方向の情報が届くので、それを新しく用意する変数に代入する
    GameData クラスがアタッチされている GameData ゲームオブジェクトはシーン遷移しても破棄されないゲームオブジェクトなので、これら変数はシーン遷移しても保持される情報になる
 4.Battle シーンへ遷移する
 5.Battle シーンから再度 Main シーンへ遷移する。この Main シーンは新しい Main シーンなので、プレイヤーキャラの位置や方向は初期位置になる
 6.PlayerController クラスの Start メソッドに新しく処理を追加し、もしも Battle シーンから Main シーンに遷移している場合に限り、プレイヤーキャラの位置と向いている方向の情報を
    GameData クラスに保持されているプレイヤーキャラの位置と向いている方向の情報を参照して、配置を変更する処理を実装する

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

 この処理を順番に実装していくようになりますが、処理を実装する順番と、実際に行われる処理の順番は前後して構いません
(逆説的に考えると、この順番にとらわれてしまうとスクリプトの修正が難しくなります。)

 つまり、上記の1〜6の処理を実装するにあたって、1から順番に実装しなければならない、ということはないということになります。
そしてどちらかというとゴール地点である6の処理から実装を行っていった方が処理は実装しやすいケースが多いです。

 これは新しく追加する処理を、1から順に実行していくことに関係しています。
先の処理を見ていただくと、6の処理には新しく追加する処理があります。
また、2の処理を実行するためにも GameData クラスに事前に新しい処理を実装しておく必要があります。

 このように処理の全体の流れをあらかじめ把握して設計しておくことにより、どういった順番でスクリプトの修正をしていけばよいのか、
その情報を自分で管理することが出来るようになります。この把握をせずに、たとえば2から処理を書き始めてしまったとしても
ここで処理したい内容が GameData クラスにはまだ実装されていないため、実装ができない状態になってしまいます。

 これが実際の処理の流れの順番と、スクリプトを修正したり、作成したりする順番が前後するという意味になります。



 上記の処理の流れをふまえてスクリプトを修正を考えると、次のような順番がやりやすいでしょう。

 1.GameData クラスを修正して、プレイヤーキャラの位置と向いている方向の情報を保持するための変数を新しく用意する。
    また、EncountManager クラスから実行される「プレイヤーキャラの位置と向いている方向の情報を保持する処理」を記述したメソッドを用意する
    さらに、PlayerController クラスから実行される「プレイヤーキャラの位置と向いている方向の情報を参照する処理」を記述したメソッドを用意する

 2.PlayerController クラスを修正して、GameData クラスの情報を参照して、Battle シーンから Main シーンへと遷移した場合に限り、
    プレイヤーキャラの位置と向いている方向の情報を設定する処理を Start メソッド内に用意する。
    Start メソッドに用意する理由はゲーム開始時にプレイヤーキャラの位置と向いている方向の情報を設定しておく必要があるため
    また、新しく向いている方向の情報を外部のクラスで利用できるようにするメソッドを用意する。なぜならこの情報は private 修飾子であるのでそのままでは外部のクラスで利用ができないため

 3.EncountManager クラスを修正して、エンカウントが発生したときに Battle シーンへと遷移する前に GameData クラスに準備した
    「プレイヤーキャラの位置と向いている方向の情報を保持する処理」を行うメソッドを実行して、それらの情報を GameData クラスに保持させる
    このとき、PlayerController クラスに用意した向いている方向の情報を取得するメソッドも利用して方向の情報を取得して利用する

 前回の実装のように、どちらの順番で書いても片方のスクリプトに一時的にエラーが発生するようなもの(競合)については、それを把握した上でどちらから修正しても構いません。
 
 今回はそのように競合してしまう処理はないので、上記の順番でスクリプトの修正を行っていければ、設計した処理の流れを実装することが出来ます。

 処理の流れはそれとして、その流れを実装していくには、果たしてどのスクリプトから実装を考えていけばよいか、その順番は別として考えることが大切です。
 

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


 すべてのスクリプトでは、そのスクリプトに必要な情報はなんだろうか、という点を考えてから修正を行っていくようにします。

 プレイヤーキャラの位置の情報と向いている方向の情報を保持するための変数とメソッドを作成します。

 シングルトンクラスの学習のため、GameData クラスを参照する処理については、public 修飾子の変数と、private 修飾子の変数の両方を利用しています。
public 修飾子の変数を参照する際の書式と、private 修飾子の変数を参照する際の書式の違いをしっかりと理解してください。

 SetEncountPlayerPosAndDirection メソッドを実行することで、引数に届いた情報を GameData クラス内に用意した変数に保持します。

 Get 〜 で始まる2つのメソッドを実行することで、それぞれの情報を実行した呼び出し元へと戻すことが出来ます。

 引数を利用した処理、戻り値を活用した処理について理解を深めていきましょう


GameData.cs

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


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


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


 Battle シーンから Main シーンを読み込んだ際にプレイヤーキャラの位置と向いている方向をエンカウント発生時の状態に設定する処理を追加します。
また、そのためにはキャラの向いている方向の情報を外部のクラスで参照する必要があります(そうしないと方向の情報を外部クラスで保持できなくなります)ので、
専用のメソッドを用意して、そのメソッドを外部のクラスで実行してもらうことにより、向いている方向の情報を外部クラスへ渡すようにします。



 注意するべき点があります

 それは最初にゲームを実行した場合にはエンカウントした位置と方向の情報はありませんので、
ゲームスタート時の Main シーンでは、スタート地点として設定している位置にプレイヤーキャラは設置されるようにするということです。

 そして、エンカウントした後に、Battle シーンから Main シーンへの遷移時のように、
エンカウントした位置と方向の情報がある場合に限り、プレイヤーキャラの位置と方向を設定し直すようにする必要があります。

 こういった制御処理を行うにはどのような処理を実装すればよいか、ロジックをしっかりと考えてながら処理を読み解いてみてください。


PlayerController.cs

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


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


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


 前回と同じように TODO 機能の部分にプレイヤーの位置と方向を保持する処理の実装を行います。

 情報を保持させるスクリプトは GameData クラスになります。GameData クラスに先ほど用意したメソッドを実行して、
プレイヤーの位置と向いている方向の情報を GameData クラスに保持させてください

 EncountManager クラスは PlayerController クラスを変数に代入していますので、自由に扱うことができます。
位置情報については、PlayerController クラス経由で Transform コンポーネントの Position を対象としてください

 向いている方向については PlayerController クラス内にある lookDirection 変数が扱っていますが、この変数は private です。よって外部クラスからは直接参照できません
そのため先ほど、PlayerController クラスにこの情報を取得するためのメソッドを準備しましたので、そのメソッドを実行して情報を取得してください。



 なお保持させる情報ですが、Position の Vector2 型ではなくて Transform 型にて記録すると、
前の Main シーンの PlayerController を参照することになるため、シーン遷移時に 前のシーンの PlayerController がないためエラーで処理が停止します。

 そのため、保持させる際にも、どの情報が有効で、どの情報はエラーになるのかを精査して設定する必要があります。



 また処理を記述する順番にも注意を払ってください。GameData クラスに情報を保持させる前にシーン遷移してしまうと、
再度 Main シーンに遷移したときに、GameData クラスに情報がないため、プレイヤーキャラを戻すべき位置や方向の情報がないため、処理を実行できません


EncountManager.cs

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


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


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


 Main シーンにてエンカウントを発生させて、Battle シーンに遷移します。
Battle シーンでバトル終了用のボタンを押して、再度、Main シーンへと遷移を行ってください。
このとき、エンカウントした位置と方向を向いてプレイヤーキャラが Main シーンに配置されれば制御成功です。


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


 ここまで製作していただくとわかるかと思いますが、一見簡単そうに見えるゲーム画面上での制御も
内部的にはこれだけの処理を実装しないとゲーム画面での制御が行えないということになります。

 そのため、自分が実装したいと思うゴール地点を決めて、そこに向かって少しずつ実装を進めていくことが大切になります。



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

 次は 手順17 −アイテム用のデータベースの作成− です。