i-school - ベルトスクロールアクション プレイアブルキャラ・敵キャラのデータ取得方法


設計

 
 プレファブに用意したプレイヤー用のキャラのゲームオブジェクトは、攻撃力やHPなどのデータを保持しています。
もしも外見が同じキャラであってもこれらの値が変化するような場合、また新しくプレファブを用意するのでは大変です。

 この手順では、キャラのデータをデータベース化して用意し、それを、現在選択しているキャラに応じて攻撃力などのデータを動的にゲーム内に反映できる機能を追加します。
またこの機能を敵側にも利用して、敵のデータもプレファブから設定するのではなく、データベースを参照して変更するようにします。

 データベースにはスクリプタブル・オブジェクトを利用します。


実装の手順


 設計に基づき、以下の手順で実装を行います。

 1.MonoBehaviour クラスを継承しない、CharaType スクリプトを作成する
 2.CharaDataList スクリプタブル・オブジェクトを作成する
 3.GameData スクリプトを修正する
 4.Enemy スクリプトを修正して、CharaDataList に登録されているキャラのデータをゲーム内に反映する処理を追加する
 5.GameManager スクリプトを修正して、CharaDataList に登録されているキャラのデータをゲーム内に反映する処理を追加する
 6.PlayerController スクリプトを修正して、CharaDataList に登録されているキャラのデータをゲーム内に反映する処理を追加する
 7.ゲームを実行して動作を確認する


新しく学習する内容


 新しく学習する内容です。

 ・MonoBehaviour クラスを継承しないenumの作り方
 ・List.Findメソッド


1.MonoBehaviour クラスを継承しない、CharaType スクリプトを作成する


 ゲーム内に登場するキャラの分類を列挙子として登録します。
ここでは仮に以下のような内容で作成していますが、最初にプレイアブルキャラの種類、次に敵の種類を登録します。
自分のゲーム内容に合わせた名前を登録してください。


CharaType.cs

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


 このスクリプト・ファイルは MonoBehaviour クラスを継承していませんのでゲームオブジェクトにアタッチすることはできませんが
「CharaType.列挙子」と書くことによって、他のクラスから自由に参照することが出来ます。(CharaType.Player_A、CharaType.Enemy_C、というように)


2.CharaDataList スクリプトを作成し、スクリプタブル・オブジェクトを作成する

1.設計


 ゲーム内で利用するキャラの情報を1つのクラス CharaData にまとめて宣言します。
またそれをListにして管理を行えるようにします。


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


 ネスト(入れ子)になっている CharaData クラスがキャラのデータを扱います。
プレイヤー側と敵側で同じ内容に統一しておくことで、1つの CharaData クラスで両方の情報を登録できます。

 1つの CharaData に1つ分のキャラの情報を代入します。つまり、プレイアブルキャラの情報を2つ用意したい場合には
CharaData クラスが2つ用意する必要があります。それをListである charaDatas 変数が管理している設計です。


CharaDataList.cs

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



3.CharaDataList スクリプトを元にスクリプタブル・オブジェクトを作成する


 CharaDataList スクリプトを元にスクリプタブル・オブジェクトを作成します。

 Unityの左上のメニューから Assets => Create => Create CharaData を選択してください。
新しくスクリプタブル・オブジェクトが作成されます。名前はそのままで問題ありません。


<手順動画>
https://gyazo.com/0a3beaf71c33136886deca1d28389c6b


フォルダ画像



4.スクリプタブル・オブジェクトの設定を行う


 CharaDataList スクリプタブル・オブジェクトの設定を行います。
CharaDataList スクリプタブル・オブジェクトを選択するとインスペクターで設定を行えます。


CharaDataList スクリプタブル・オブジェクト インスペクター画像



 Listになっていますので、Size を 0 => 3 に変更し、3つ程、キャラのデータを登録してみてください。
また CharaType については Enum ですので、プルダウンメニューより登録されている列挙子から選択するようになります。


Size を変更した際のインスペクター画像



CharaData をいれた際のインスペクター画像(自分のデータに合わせて設定してください)



 以上で設定は完了です。


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

1.設計


 ゲーム内でプレイヤーが使用するキャラを選択した際に、GameDataに新しく用意した currentCharaNo 変数に登録を行うようにします。
こうすることで、選択シーンにおいて選択したキャラの情報を、ゲームのバトルシーンに遷移した際にも参照して利用することが可能になります。

 この情報を元にしてスクリプタブル・オブジェクト(データベース)から必要なキャラの情報を取得して利用するようにします。
例えば、プレイヤーがキャラ2を選択した場合には、currentCharaNo 変数には 1 を登録します。その値とデータベースの CharaData の持つ no 変数とを照合し、
合致した no 変数を持つ CharaData をキャラの情報としてゲーム内で利用するように反映処理を行います。


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


 新しく変数を4つ宣言します。内容については各変数のコメントを参照してください。
またメソッドを3つ追加して、データベースを扱うために必要な処理を追加しています。

 SetUpPlayableCharaData メソッドを実行することで、playableCharaData 変数に今回のバトルで利用する CharaData を取得して代入します。
取得する際には GetCharaData メソッドが実行されて、その戻り値を利用しています。

 SetUpCurrentCharaNo メソッドを実行することで、currentCharaNo 変数に、今回のバトルで使用するキャラの番号を登録します。
現在はまだ未使用ですが、このメソッドをキャラ選択時に呼び出すことによって、GameDataにキャラの番号を登録できます。
この処理を行わないと、データベースに登録してある CharaData の番号と照合が出来ないため、ゲーム内にデータベースの反映が行えません。

 そのため今回は、ゲーム開始前に currentCharaNo 変数の値を直接インスペクターで操作を行って、データベースが反映されるかをチェックしておきます。


GameData.cs

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



3.<List.Findメソッド>


 Listには Find というメソッドがあります。
List 内の要素を先頭から検索して、指定した条件に合致した最初の要素1つを取り出して戻り値として返してくれる処理です。
これは foreach 文で記述する内容と同じ処理を1行で処理できます。

  charaDataList.charaDatas.Find(x => x.no == charaNo);


  foreach (CharaDataList.CharaData charaData in charaDataList.charaDatas) {
      if(charaData.no == charaNo) {
          return charaData;
      }
  }

 この2つの処理は同じ内容になります。
最初に合致した値が対象になりますので、まとめて複数取得したい場合や、合致する条件を満たす要素が複数ある場合には、この処理だけでは実装できません。


参考サイト
Samurai Blog様
【C#入門】Listの要素を検索するFindの使い方(FindAll/FindIndex)
https://www.sejuku.net/blog/45252


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


 スクリプトを修正したので、GameData ゲームオブジェクトを選択して、インスペクターから設定を行います。


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



 playableCharaData 変数はゲームの実行時に代入される情報になりますので、他の3つの情報についてアサインを行います。


 currentCharaNo 変数には、プレイアブルキャラの番号を登録します。デバッグを行いますので、最初は 0 のままで構いません。

 charaDataList 変数には CharaDataList スクリプタブル・オブジェクトをドラッグアンドドロップしてアサインします。

 usePlayableCharaCount 変数には、プレイアブルキャラの総数を登録します。
今回はスクリプタブル・オブジェクトに2つ分のプレイヤブルキャラの情報を登録したので 2 を設定します。

 
<手順動画 スクリプタブル・オブジェクトのアサイン>
https://gyazo.com/718a6a05cddadbc7766b99c44ba16dac


GameData ゲームオブジェクト アサイン後 インスペクター画像



5.スクリプタブル・オブジェクトの読み解き方


 CharaData クラスをListで管理していますので、CharaData クラスの情報に対してアクセスしたい場合には、
スクリプタブル・オブジェクトの代入されている変数の指定後に、そのスクリプタブル・オブジェクトの管理しているListの変数を指定し、それから個別のCharaData の指定を行う書式になります。

charaDataList.charaDatas[参照したいCharaDataの要素番号].CharaData クラスの持つ変数


 また、CharaData クラスは CharaDataList クラスの子クラスですので、単体では型として書けません。
必ず親クラスである CharaDataList を付けて書く必要があります。

宣言する場合
CharaDataList.CharaData chara


4.Enemy スクリプトを修正して、CharaDataList に登録されているキャラのデータをゲーム内に反映する処理を追加する


 Enemyスクリプトを修正して、データベースの値を参照できるように処理を追加します。

 前提として、敵のゲームオブジェクトがプレファブであり、スクリプトからクローンを生成する処理が必要になります。
今回は Enemy スクリプトと SetUpEnemy メソッドの修正を行っていますが、ご自分の敵のデータを管理しているスクリプトに、以下の処理を追記してください。


Enemy.cs

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



5.GameManager スクリプトを修正して、CharaDataList に登録されているキャラのデータをゲーム内に反映する処理を追加する

1.設計


 データベースの処理を実装するために、新しい変数を2つ宣言します。
プレイヤーを生成するために必要なプレファブの情報をアサインする変数と、UIManager スクリプトの情報をアサインする変数を用意しています。
このクラスでは UIManager を利用していませんが、PlayerController においてアサインが必要になるため、このクラスを経由してアサインを行っています。

 既存のメソッドについては、2つのメソッドで処理を追加・修正しています。

 Start メソッド内に処理を修正し、GameDataにバトルで使用するキャラのデータの取得と、プレイヤーの生成を追加します。
 GenerateEnemyメソッド内の処理を修正し、生成された敵がデータベースの情報を参照できるように、SetUpEnemy メソッドに引数を追加します。

 新しく CreatePlayer メソッドを追加して、プレファブからプレイヤーを生成し、その後、キャラのデータを利用して
選択されているキャラのデータを持ったプレイヤーを作成します。


 それ以外の処理については割愛しています。


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



GameManager.cs

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



3.GameManager ゲームオブジェクトを設定する


 スクリプトを修正しましたので、アサイン情報を追加します。

 PlayerPrefab 変数には、作成してあるプレイヤー用のキャラのゲームオブジェクトのプレファブをドラッグアンドドロップしてアサインしてください。

 UIManager 変数には UIManager スクリプトのアサインが必要になりますので、ヒエラルキーにある UIManager ゲームオブジェクトをドラッグアンドドロップしてアサインしてください。


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



6.PlayerController スクリプトを修正して、CharaDataList に登録されているキャラのデータをゲーム内に反映する処理を追加する


 InitPlayer メソッドを修正して、GameData スクリプトに登録されている、今回のバトルで使用するキャラのデータベースの情報を元に、各能力値の値を設定します。
 また Start メソッドにおいて InitPlayer メソッドを呼び出している場合には、その処理はコメントアウトしてください。

 それ以外の処理については割愛します。


PlayerController.cs

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



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

1.プレイヤー用のゲームオブジェクトの設定を行う


 プレイヤー用のキャラのプレファブを確認して、データベースから取得出来る情報についてはすべて初期化しておきます。(Enum は空にできませんので、適当な列挙子にしておきます)
こうしておくことにより、データベースから指定されたキャラのデータがちゃんと反映されるかどうかを確認することが出来ます。


キャラ用のプレファブのデータ(参考画像ですので、変数名などは異なっている場合があります)


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


 続いて、GameData ゲームオブジェクトの設定を行います。まずは currentCharaNo 変数を 0 にした状態で、ゲームを実行してみましょう。





 下記の画像のように、currentCharaNo とデータベースの照合を行った結果が、PlayableCharaData に自動的に反映されれば成功です。




3.GameDataの設定値を変更して確認する


 次は、currentCharaNo 変数の値を 1 に変更してみましょう。データベースの CharaData.no が 1 であるデータを取得できれば成功です。
 

GameData ゲームオブジェクトの設定



実行時(PlayableCharaData の値がすべて CharaData の値で更新される)



実行時の Player ゲームオブジェクトの設定値に、GameData のPlayableCharaData の値が反映されている



4.敵のゲームオブジェクトにデータベースの値が反映されるか確認する


 プレイヤーへのデータベースの反映が問題なければ、次は敵側の各データが、データベースの値を参照できるかどうかを確認します。

 最初にプレイヤーのプレファブと同じように、敵のプレファブを選択し、データベースの値を参照するものについては初期化しておきます。





 ゲームを実行して敵を生成させてみてください。インスペクターで各値を確認し、敵用のデータが代入されていれば成功です。


実行時のインスペクター画像


5.敵の種類を増やして、自動的に反映されるか確認する


 スクリプタブル・オブジェクトの Size を 3 => 4 に変更して、2種類目の敵のデータを Element 3 として追加します。


スクリプタブル・オブジェクトの設定画像



 また、GameManager ゲームオブジェクトを確認し、2種類目の敵のプレファブがアサインされているかどうかを確認します。


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



 最後に StageList スクリプタブル・オブジェクトを確認して、AppearNum の値を 0、あるいは 1 に設定してください。
この値が敵の種類になっていますので、0 の場合にはデータベースの1種類目(CharaDataListスクリプタブル・オブジェクトのElement 2)に登録されている敵のデータが参照され
1 の場合には 2種類目(CharaDataListスクリプタブル・オブジェクトのElement 3) に登録されている敵のデータが参照されます。


StageList スクリプタブル・オブジェクトの AppearNum(ここでは 1、0、1)



 ゲームを実行して敵の種類が変わった場合でも、データベースからデータを参照できているかを確認してください。


同じ敵のプレファブのデータが、異なるデータベースの値を参照するため変化する



 以上でこの手順は終了です。おつかれさまでした。