ユニティちゃんのRPGを作ってみよう9ーアイテムやステータス等のデータ作成ー

今回はユニティちゃんのRPGで使用するアイテムやステータス等のデータの作成についてやっていきたいと思います。

この記事から後のいくつかの記事はアイテムやステータスのデータを使ってコマンド画面での処理を作っていく事になるので少し難しいというか処理が複雑になってきます。

細かい部分の説明は入れているつもりですが、難しいのは否めません。

単純にわたくしのやり方が複雑にしている可能性もありますが・・・、わたくしのやり方がベストプラクティスではないのでより最適でわかりやすいやり方を模索してみてください。

前回はユニティちゃんの操作をゲームコントローラーで出来るようにしました。

ユニティちゃんのRPGを作ってみよう8ーゲームコントローラーに対応するー
ユニティちゃんのRPGでゲームコントローラー(PS3コントローラーやPS4コントローラー)でゲームの操作が出来るようにしていきます。

ユニティちゃんのRPGを作ってみようの他の記事は

ユニティちゃんのRPGを作ってみよう
ユニティちゃんのRPGを徐々に作っていくカテゴリです。

から見ることが出来ます。

スポンサーリンク
スポンサーリンク

アイテムやステータスのデータ管理をどうするか?

まずは最初にアイテムやステータスのデータをどうやって取り扱うかを考えます。

アイテムやステータスのデータは簡単に言えばテキストの集まりなので、外部ファイルにアイテムデータやステータスデータを保持して置き、必要に応じてそれらのデータを取得したり書き換えたりする事で実現出来ます。

ただ外部ファイルの読み込みや書き込みをするとなるとファイル操作が必要になるので慣れていない人には難しいところです。

外部ファイルにデータを保存するのではなくゲーム中にデータを保持しているクラスをインスタンス化してそこからデータの取得、書き換えをする方法もあります。

この場合はC#スクリプトをMonoBehaviourクラスを継承してクラスを作成するのではなく、objectクラスを継承する形でクラスを作成するか何も継承しない(デフォルトでobjectクラスを継承する事になる)形でクラスを作成する必要があります。

この場合はゲーム実行開始とともにクラスをインスタンス化しデータを取得したり書き換えたりすることは出来ますが、スクリプトの中にデータの情報を入れる必要があります。

その他、MonoBehaviourクラスを継承してクラスを作成し、その中でデータを保持しておき何らかのゲームオブジェクトに取り付けて利用する方法もあります。

この方法でもアイテムやステータスのデータは扱えますが、視覚的に分かり辛いというのがあります。

そんな時に便利なのがUnityのScriptableObjectです。

クラスをScriptableObjectクラスを継承して作成すると予めそのクラスをアセットファイルとしてファイル形式にしておくことが出来るので、ゲーム開始前からデータファイルとして存在させることが出来ます。

ScriptableObjectクラスを継承しなくてもデータの取り扱いは出来ますが、それらのデータを視覚的に確認するにはスクリプトの中身を見る必要があり実際のデータはゲームが開始するまで作成されません。

ScriptableObjectクラスを継承してクラスを作成すると存在するアセットファイルとしてそのデータを取り扱う事が出来るのでゲーム開始前にデータをセットし視覚的にわかりやすくなります。

ただ注意が必要なのがScriptableObjectクラスから作成したアセットファイルは視覚的に確認出来るアセットファイルとして存在しますが、エディターを開きなおしたりゲームをやめたあと再び再開した時は初期値に戻ります。

これは元のデータをインスタンス化して視覚化しているのがアセットファイルで次回開いた時はインスタンス化するところから始まっているからなのかも?

データを次回以降にも持ち越すにはScriptableObjectから作ったアセットファイルのデータを外部ファイル等に保存する必要があります(これは別の記事でやります)。

ScriptableObjectについては

UnityのScriptableObjectを使う
UnityのScriptableObjectを使うと、メモリの節約が出来たり、シーン間の移動でデータを共有するのが簡単になります。この記事では基本だけを記述しています。

上の記事を参照してください。

ScriptableObjectはシーン間の遷移をする時にも使っています。

ユニティちゃんのRPGを作ってみよう7ーワールドマップの作成とシーン間の遷移ー
ユニティちゃんのRPGでワールドマップシーンの作成とワールドマップシーンと村のシーンの移動が出来るようにしていきます。

これらの事を考慮してアイテムやステータス等のRPGで使用するデータ群はScriptableObjectクラスを継承して作成し、予めアセットファイルとして見てすぐわかるようにしておきます。

ユニティちゃんRPGで使用するデータの作成

ユニティちゃんRPGで使用するデータを作成するにはそのデータを作成するスクリプトが必要になります。

そこからアセットファイルデータを作成出来ます。

なので、わかりやすくする為にアイテムデータを作成するスクリプトはAssets/RPG/Scripts/Itemフォルダ、ステータスデータを作成するスクリプトはAssets/RPG/Scripts/Statusフォルダ内に作成する事にします。

それらのスクリプトから作成したアセットファイルはアイテムデータはAssets/RPG/Data/ItemDataフォルダ、ステータスデータはAssets/RPG/Data/Statusフォルダ内に作成します。

スクリプトやアセットファイルの保存先は各々わかりやすい階層に移動させてください。

キャラクターステータスデータの作成

最初にキャラクターのステータスを保持するデータを作成したいと思います。

キャラクターステータスは味方のキャラクターのステータスと戦闘時の敵のステータスと似たようなステータスを持つけど一部違うフィールドだったりメソッドを持たせたい事もあります。

そんな時に便利なのが継承で、継承を使うと継承元の持っているフィールドやメソッドを継承先のクラスが持つことが出来ます。

ただなんでもかんでも継承をすればよいというわけではなく『子(継承先)は親(継承元)である』という条件に合致する時だけ使用します。

例えば、親のキャラクターステータスをCharacterStatusクラスとして作ったならば、それを継承してAllyStatus(味方のステータス)とEnemyStatus(敵のステータス)を作成します。

キャラクターステータスを継承する

『味方のステータスと敵のステータスはどちらもキャラクターステータスである』というのが成り立つので継承を使えます。

まったく関係ないHuman(人間)クラスを継承してSword(剣)クラスを作った場合は『剣は人間である』というのが成り立たない為、継承は使いません。

プログラム的に継承は使えますが意味が分からなくなるので使わないようにします。

リアルな世界で成立しているかどうかで判断すると良いようです。

今回の場合は特別に継承を使ってキャラクターステータスを作らなくても良いような気もしますが、色々試してみましょう。(-ω-)/

上の図では子クラスから親クラスへと矢印が向いていますが、これは子クラスは親クラスのフィールドや変数に入れることが出来るのでこの方向に矢印が向いています。

例えばBaseStatusクラスを継承してStatusAとStatusBクラスを作ったとします。

上のようにBaseStatus型の変数にインスタンス化したStatusA、StatusBを入れることが出来ます。

BaseStatusではフィールドとコンストラクタ、特殊攻撃のメソッドを持っています。

SpecialAttackメソッドはvirtualを付け仮想メソッドとして定義し、子要素でoverrideされない場合は親クラスのSpecialAttackメソッドが実行されます。

StatusAとStatusBではoverrideでSpecialAttackメソッドを定義しているので子クラスのSpecialAttackメソッドが実行されます。

StatusAとStatusBではコンストラクタで受け取ったキャラクターの名前と、特殊攻撃の名前を:baseを使って親のコンストラクタにそのまま引数付きで渡しています。

StartメソッドではBaseStatus型の配列にStatusAとStatusBを入れているのでBaseStatusをforeachでループさせStatusAとStatusBのメソッドを呼び出せます。

同じSpecialAttackメソッドの呼び出しを行っていてもStatusAとStatusBでは実行する処理が違っています。

これを多態性と言います(実行するメソッドは同じでも違う振る舞いをする)。

継承や多態性についてはプログラムの書籍などを参照してみてください。(´Д`)

継承元のキャラクターデータを作成するスクリプトの作成

まずは継承元のCharacterStatusスクリプトを作成します。

CharacterStatusスクリプトはキャラクターが持っているべきフィールドとそれを設定・取得するメソッドを持つようにします。

Assets/RPG/Scripts/Statusフォルダ内に新しくCharacterStatusスクリプトを作成します。

非常にスクリプトが長いですが中で行っていることは単純です。

セッターやゲッターを容易するよりもプロパティとして作った方が楽かもしれません。

CharacterStatusスクリプトはScriptableObjectクラスを継承して作成し、

キャラクターが持っているべきステータスを保持し、それらの値を設定したり取得したりするメソッドが用意されているだけです。

SetHpとSetMpでは少し分かりずらい処理をしていますので、見ておきます。

SetHpでは引数で受け取ったhpをキャラクターのHPに入れる処理をしますが、まずキャラクター自身の最大HPと引数で受け取ったhpをMathf.Minで最小値を取得します。

これはHPに設定する値が最大HPを超えないようにする為です。

次に0とMathf.Minで取得した値をMathf.Maxで大きい方の値を取得します。

これは設定するHPが0より小さい値にならないようにする為の処理です。

SetMpでも同じことをしています。

クラス定義の前にアトリビュートを取り付けているので見ていきます。

[Serializable]アトリビュートを取り付ける事で直列化(データ転送可)が可能なクラスとしています。

直列化が出来るとはバイト列に変換が出来るということです。

バイトとは8ビットの事で、1ビットは2進数(0か1)というコンピューターの最小単位の情報です。

つまり、データを0か1かの情報に変換してそれを送信して送信先で復元出来るようにする為に直列化をしています(たぶん)。

classの前にabstractを付けて抽象クラスにし、CharacterStatusクラス自体をインスタンス化出来ないようにします。

これはCharacterStatusクラスは継承に使う元のクラスであってCharacterStatusクラス自体をインスタンス化して使わせない為です。

今回の場合は別にインスタンス化しても問題はないですが、とりあえずCharacterStatus自体をインスタンス化出来ないようにしておきます。

キャラクターが持つステータスをフィールドとして用意し、それらにはSerializeFieldアトリビュートを付けインスペクタ上で設定出来るように直列化可能にしています。

継承先の味方のステータスを作成するスクリプトの作成

継承元のクラスCharacterStatusが出来たのでこれを継承し、味方のステータスファイルを作成するクラスを作ります。

Assets/RPG/Scripts/Statusフォルダ内にAllyStatusクラスを作成します。

アトリビュートでCreateAssetMenuを取り付けUnityのAssetメニューからAllyStatusクラスのファイルを作成する事が出来ます。

AllyStatusはCharacterStatusクラスを継承しているのでCharacterStatusクラスが持っているフィールドとメソッドを持っています。

味方が持つべきフィールドやメソッドを追加しています。

少し難しい処理をしている部分を見ていきます。

GetSortItemDictionaryメソッドはキャラクターが保持しているアイテムのDictionaryデータをDictionaryのキーであるItemの平仮名の名前でソートをしたデータを返します。

アイテムデータを平仮名の順で並べ替えたデータを使いたい場合に使います。

OrderByの引数ではラムダ式を使って並べ替えの基準を指定しています。

itemDictionaryのキーであるItemクラスのGetHiraganaNameメソッドでそのアイテムの平仮名の名前を取得しそれを使ってソートしています。

C#でデリゲートとラムダ式を使ってみる
C#のデリゲートとラムダ式をUnity付属のMonoDevelopで使用してみます。

OrderByメソッドで得られるのはIOrderdEnumerable<KeyValuePair<Item, int>>型になります。

ソートしたデータを取得しない場合はGetItemDictionaryメソッドを使います。

キャラクターデータの作成

キャラクターデータを作成するスクリプトが出来たので、Assets/RPG/Data/Statusフォルダ内に移動し右クリックからCreateAllyStatusを選択します。

ユニティちゃんRPGのキャラクターステータスデータの作成

上の画像では他のユニティちゃんRPGデータの作成項目が出ていますが、これはわたくしの都合上出ているだけなのでその他の項目は表示されていないはずです。

CreateAllyStatusを選択するとAssets/RPG/Data/Statusフォルダ内にAllyStatusという名前のアセットファイルが作成されます。

AllyStatusファイルを選択した状態でF2キーを押し名前をUnityChanStatusと変更します。

同じようにもう一つAllyStatusファイルを作成し名前をYujiStatusと変更します。

ItemDictionaryのインスペクタでの表示

アイテムとその数はDictonaryクラスを使って(Dictionary<Item, int>)インスペクタで設定したいところですが、Dictionaryクラスの場合はキーと値が別々に表示されます。

ユニティちゃんRPGのSerializableDictionaryを使わない例

(上の例ではデータが既に設定されていますが、通常はスクリプトで設定したデフォルト値が設定されているはずです)。

level、strikingStrength、magicPowerのパラメータが表示されてませんが、これは後でパラメータを加えたため画像を作る段階ではなかった為です。これ以降の画像も同様です。

そこでアセットストアで「SerializableDictionary」で検索して出てくるアセットをインポートします(無料のやつ)。

インポートしたらAssets/RPG/Scripts/Itemフォルダ内にItemDictionaryスクリプトを作成します。

ItemDictionaryクラスはSerializableDictionaryを継承して作成します。

Assets/RPG内にEditorフォルダを作成しその中にSerializableDictionaryスクリプトを作成します。

これでUnityChanStatusのItemDictionaryはキーにItem、値にそのアイテムの数が対応して表示されます。

ユニティちゃんRPGでSerializableDictionaryを使った例

ユニティちゃんとその仲間である大鳥ゆうじのステータスファイルが出来ましたが、装備する武器や鎧、持っているアイテム等はまだ作っていないのでそれらを作っていきます。

アイテムデータの作成

これからアイテムデータを作成していきますが、やり方としてはキャラクターデータの作成と同じようにスクリプトを作成しUnityのAssetメニューからそのデータを作成します。

アイテムは武器、鎧、HP回復アイテム、状態回復アイテム、貴重品などいくつか種類がありますのでアイテムの種類を選択出来るようにします。

ItemクラスはScriptableObjectクラスを継承して作成します。

Typeはアイテムの種類を表します。

WeaponAllは誰でも装備出来る武器で、WeaponUnityChanはユニティちゃんしか装備出来ない武器です。

kanjiNameは漢字を含めたアイテムの名前でhiraganaNameは平仮名でのアイテム名を設定します。

なぜ二つのアイテム名を設定出来るようにしたかというと、複数のアイテムデータを取得する時に平仮名でアイテムデータをソートするのはそのまま出来ますが、漢字を含めると別に自分で実装しないとソートが出来ないからです。

Itemクラスでは漢字名、平仮名名、アイテムの情報、アイテムの強さというアイテムが持っているべき最低限の情報を持たせる事にしました。

アイテムクラスが出来たので、個々のアイテムを作成します。

データはそれぞれ異なるフォルダに作成します。

Assets/RPG/Data/ItemData/Armorに鎧のデータ
Assets/RPG/Data/ItemData/Recoveryに回復アイテムデータ
Assets/RPG/Data/ItemData/StateRecoveryに毒と痺れを回復するアイテムデータ
Assets/RPG/Data/ItemData/Valuablesに貴重品データ
Assets/RPG/Data/ItemData/Weaponに武器のデータ

個々のフォルダ内で右クリックからCreateItemを選択しデータを作成します。

ユニティちゃんRPGで使用する普通の剣のデータ

上のようにデータを作成し、UnityChanStatusやYujiStatusのEquipWeaponやEquipArmor、ItemDictionaryにアイテムをドラッグ&ドロップか◎を押して設定しておきます。

アイテムデータの保存先をフォルダ毎に分けたのでドラッグ&ドロップが出来なくなりますが、これを出来るようにする為にインスペクタをロックして行います。

UnityChanStatusデータにアイテムを設定する時はUnityChanStatusを選択し、インスペクタで鍵のマークを押します。

UnityChanStatusのインスペクタで鍵マークを押す

これでUnityChanStatusのインスペクタが固定された状態で他のフォルダに移動し武器のデータや鎧のデータが選択出来るので、それをドラッグ&ドロップします。

インスペクタをロックして他のデータをドラッグ&ドロップする

データを設定したらインスペクタの鍵のマークを押してロックを解除しておくことを忘れないでください。

でないとインスペクタにずっと同じゲームオブジェクトの情報が固定され、Unityが正常に動作しなくなった!?とパニックに陥る可能性がありますので・・・・(-_-)

今回はあらかじめユニティちゃんと大鳥ゆうじのステータスデータをアセットファイルに設定していますが、通常はゲーム開始時の値を設定しておき、CharacterStatusスクリプトのSetItemDictionaryメソッドを使って入手したアイテムを追加するようにします。

一つ注意が必要なのがこの記事の最初の方でも言及しましたが、キャラクターステータスやアイテムステータス等のアセットファイルの中身はゲームを終了したり、Unityエディター上でスクリプトで値を変化させてもゲームを終了したり、Unityエディターを閉じると元の値に戻ります。

単純にスクリプトから作成したインスタンスをファイルとして表示しているだけだからです(たぶん)。

なのでゲームのデータやロード機能を作る場合はPlayerPrefsを使ってレジストリにデータを保持しそれを読み込んだり、外部ファイルなどにデータを保存し再開する時に読みだす必要があります。

ここら辺は

Unityでゲームデータのセーブ・ロードを行う方法
Unityのゲームで進行具合やキャラクターのステータスをデータとして保存しておき、それらのデータを読みだして続きから再開するというのは必須の機能になっています。 その為に今回はゲームデータのセーブとロードが出来るようにしてみたいと思います。

を参照してみてください。

WebGL形式(ブラウザ)でのゲームの場合は外部ファイルが使えないので、サーバーにデータを保存出来るようにしたりする必要があります。

昔ながらのブラウザゲームのようにクッキーにデータ保存も出来るのかな?(-_-)

終わりに

今回はキャラクターのステータスとアイテムデータの作成をしました。

今回はアセットファイルとしてデータを作成しただけなので次回はキャラクターのステータスデータを取得して表示する機能を作成していきたいと思います。

ユニティちゃんライセンス

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

コメント

タイトルとURLをコピーしました