i-school - アイテムの情報をスクリプタブル・オブジェクトに登録して利用する
 2回の手順に分けて学習します。
複数の機能のベースとなる処理になります。



設計


 アイテムの種類が多くなってくると、ゲームオブジェクトでのデータ管理が煩雑になってきます。
その都度、ゲームオブジェクトを選択してインスペクターの値を設定するのは大変な作業になります。

 Unityではスクリプタブル・オブジェクトというデータベース用の機能がありますので
この機能を利用してアイテムの情報をデータベースで一元管理し、必要な時に情報を取り出せるように用意しておくようにします。

 こちらの使い方を含めまして、データベースをどのようにゲーム内のアイテムのデータとして反映して利用していくか、手順をまとめています。


実装手順


 以下のような順番で実装を行っていきます。
2つのパターンがあり、この手順では共通する部分を作成します。

<1.アイテムの生成や効果内容にスクリプタブル・オブジェクトを利用する>
 1.アイテムの情報を設計する
 2.スクリプタブル・オブジェクト用の ItemDatasList スクリプトを作成する
 3.【2】のスクリプタブル・オブジェクトを元に、スクリプタブル・オブジェクトのアセットを作成する
 4.スクリプタブル・オブジェクトにアイテムの情報(値)を登録する
◇5.スクリプタブル・オブジェクトを管理する DataBaseManager スクリプトを作成する
◇6.ItemBase スクリプトを作成する
◇7.Item_RecoveryLife スクリプトを作成する
◇8.アイテムのプレファブに ItemNo を設定する



<2.スクリプタブル・オブジェクトを活用してアイコン情報を設定する>
 1.アイテムの情報を設計する
 2.スクリプタブル・オブジェクト用の ItemDatasList スクリプトを作成する
 3.【2】のスクリプタブル・オブジェクトを元に、スクリプタブル・オブジェクトのアセットを作成する
 4.スクリプタブル・オブジェクトにアイテムの情報(値)を登録する
◇5.スクリプタブル・オブジェクトを管理する DataBaseManager スクリプトを作成する
◇6.アイコン配置用のゲームオブジェクト、および、アイコン用のゲームオブジェクトを作成する
◇7.ItemIconDetail スクリプトを作成する
◇8.アイコンのゲームオブジェクトに ItemIconDetail スクリプトをアタッチして設定を行い、プレファブにする

 どちらの機能についても、1〜5は共通する内容になっています。

 このうち、今回の手順では、【1】〜【4】までを扱います。


<スクリプタブル・オブジェクトとは>


 キャラやエネミー、アイテムなどのデータなどを扱う場合に、複数のデータを1つのまとまりとして管理できるデータベースのようなものがあると扱いが楽になります。

 Unityにはスクリプタブル・オブジェクトという機能(こちらはアセットとしてデータベースを扱う方法)がありますので、今回はこの機能を利用していきます。

 スクリプタブル・オブジェクトを作成するためには、専用のスクリプトを作成する必要があります。その作成方法を学習します。

 今回作成するスクリプタブル・オブジェクトはアイテムのデータを管理する目的で作成を行います。
そのため、スクリプト内には、アイテムの1つ分のデータをまとめるための ItemData クラスを用意します。

 ItemData クラスは、アイテム1つ分のデータを1つにまとめている情報群です。
現在はアイテムの名前や効果値といった値を個別にゲームオブジェクト内に設定していますが、これらの情報を1つのデータ群としてまとめて管理するためのクラスになります。

 ItemData クラス内に用意した変数がアイテムのデータになりますので、どういった情報を持たせたいのか、何が必要かを考えてみましょう。
アイテムの効果値、エフェクトの種類といった情報がそれらに当たります。

 この ItemData クラスは、ゲームに登場するアイテムの種類数だけ用意することになりますので、それを管理するために List 機能を利用します。

 この2つの情報を管理して完成するのがスクリプタブル・オブジェクトになります。
どのような構成になっているかはスクリプト作成後に説明をしていますので、そちらをしっかりと学習してください。

 よって、スクリプタブル・オブジェクトを作るためには、まずは最初に ItemData クラスを作成してから、
スクリプタブル・オブジェクト用のスクリプトを作る、という風に順番にスクリプトを作成していきます。


1.アイテムの情報を設計する


 ゲーム内に登場させるアイテムの情報を定義します。今回は以下の情報を扱えるようにしようと思います。

 ・アイテムの名前
 ・アイテムの通し番号
 ・アイテムの種類
 ・アイテムの効果値
 ・アイテムの持続時間
 ・アイテム取得時のエフェクト
 ・アイテムのプレファブ

 など

 この情報をスクリプタブル・オブジェクトとして利用できるように、スクリプタブル・オブジェクトの元となるクラスを作成します。
このアイテムの情報は1つのアイテム当たりの情報になります。

ItemData.cs

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



<[System.Serializable(シリアライザブル)]属性>


 ItemData クラスの1行上には上記の宣言があります。[ ]で宣言された設定値は「属性」と呼ばれる情報になり、特別な意味を持ちます。

 今回利用している[System.Serializable]属性は、Systemに含まれている設定値であり、こちらを宣言することでクラスの情報をインスペクターに表示することが出来ます。
これを書き忘れてしまうと、インスペクターに ItemData の内容が表示されず、データをインスペクターから登録することが出来ません
[Serializable]属性を宣言する際に、using System; を宣言している場合には [Serializable] とだけ記述すれば適用されます。
using に宣言していない場合には [System.Serializable] と記述する必要があります。


2.スクリプタブル・オブジェクト用の ItemDataSO スクリプトを作成する


 【1】で設計したアイテムの情報を扱うためのスクリプタブル・オブジェクトを作成していきます。
今回はアイテム1つずつのデータを扱う ItemData クラスと、その ItemData を List にして管理するスクリプタブル・オブジェクトとを別々に作成しています。
今回は別々のファイルにしていますが、一緒のスクリプト内に作成しても構いません。



 スクリプタブル・オブジェクトを作成するために必要なスクリプトは、MonoBehabiour クラスではなく、
スクリプタブル・オブジェクト専用の ScriptableObject クラスを継承し、[CreateAssetMenu] 属性を記述することで作成可能になります。

 なお、CreateAssetMenu 属性は、エディターの拡張機能を付与するもので、スクリプタブル・オブジェクトに限ったものではありません。
この属性を付与すると、右クリックをしてメニューをした際に、新しく自分で作ったメニューを追加することができる機能です。
今回はこの機能を活用して、スクリプタブル・オブジェクトを作成する拡張機能を付与していることになります。
 


 スクリプタブル・オブジェクトでは、指定したデータを複数のデータとしてまとめて管理することが出来ます。
そのため、データベースとしての役割を果たすことが可能になっています。

 今回指定して管理したいデータはアイテムのデータ群です。
そのため、スクリプタブル・オブジェクト内に必要な情報は以下の2つです。

 1.アイテム1つ単位でのデータ(名前、効果値、アイテムのプレファブなどのデータ群)を扱う ItemData クラス
 2.アイテムのデータ(ItemData クラス)をまとめる List(リスト)

 1については、すでに ItemData クラスを作成済ですので、この情報を活用して、2の List を作成する部分がスクリプタブル・オブジェクトが必要になります。

 まずは最初にスクリプトを記述してから、内容を確認しましょう。

ItemDataSO.cs

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


 ItemData というクラスが、アイテムの情報を扱うクラスです。この情報を登録し、ゲーム内で扱えるようにします。
このクラスにはアイテムの情報を1つずつ、設定できます。

 また、itemDatasList というListの変数が、複数の ItemData をまとめて管理している値になります。


3.【2】のスクリプタブル・オブジェクトを元に、スクリプタブル・オブジェクトのアセットを作成する


 スクリプタブル・オブジェクトのスクリプトを作成しましたら、今後は、それをアセットとして生成します。
Unityの左上のメニュー内の Assets => Create => Create ItemDataSO の順番で選択してください。

 Project内に ItemDataSO という名前のスクリプタブル・オブジェクトが作成されます(アイコンの形が他と違うものです)
名前はそのままで構いません。

 クリックしてインスペクターを確認してみてください。画像のような状態が表示されると思います。これで生成は無事に完了です。



 管理しやすくするため、Project フォルダ内に直接配置するのではなく、Datas のようなフォルダを用意して、
その中でスクリプタブル・オブジェクト用のアセットを管理するようにしましょう。

 細かいことですが、こういったちょっとした配慮を怠らないことが大切です。


フォルダ画像



4.スクリプタブル・オブジェクトにアイテムの情報(値)を登録する


 インスペクターを確認して頂くと、itemDatasList の Size が 0 になっていると思います。
これは itemDatasList というスクリプタブル・オブジェクトが管理しているListの数です。
つまり、登録しているアイテムの情報数です。現在はまだアイテムの情報がないため、Listも 0 の状態です。

 この値を登録するアイテムの数に設定してみてください(今回は3を設定しています)
画像のようにElementの情報が0〜2で追加されます。

 Element とは List の要素(中身)のことです。
そのため、Element 1つが ItemData 1つになります。Element の番号は 0 から始まります。これは List 内での要素番号(index)も表しています。

 以上のことから、1つの Element には1つの ItemData クラスの内容を設定できるようになっています。
このとき、ItemData クラスの上に [Serializable] 属性を宣言しているので、ItemData クラスの内容がインスペクターに表示されています。
[Serializable] 属性を活用することによって、インスペクターからキャラ用の情報を1体ずつ、ItemData 単位で登録出来るようになっています。


 
 一番上にあるElement 0 の部分をクリックしてみてください。閉じられていた部分が開いて、情報を登録できる部分が開きます。
こちらにアイテムの情報を1つずつ登録してください。

 ItemName 変数は文字列でアイテムの名前が登録できます。

 ItemNo 変数は通し番号になりますので、int 型です。Element の番号と同じように、0 から順番に採番してください。

 efficacyValue 変数は、アイテムを取得した時の効果値です。
例えば回復アイテムであれば、回復する量を設定します。
攻撃力アップや速度アップなどは、増加量を、ペナルティのアイテムであれば減少量を設定します。
float 型ですので、小数点での指定が必要な値も設定できます。

 duration 変数は、アイテムの効果時間です。即時に回復するアイテムであれば 0 です。一定時間効果があるアイテムは、この値を設定します。
こちらも float 型ですので、小数点での指定が必要な値も設定できます。

 effectPrefab 変数には、アイテムを取得した際のエフェクト用のプレファブをアサインして登録します。

 ItemPrefab にはアイテムのプレファブをアサインして登録します。


インスペクター画像


 なお、ItemData クラス内の一番最初に記述した変数が Element の部分に表示されますので、
string 型を最初に設定することで Element の表示が文字列表記に変わります。通常は int 型です。ここは好みで使い分けてください。
(string 型のときは各 Element の初期値は数字が入らずに空白になっています)



 これでスクリプタブル・オブジェクトへのアイテムの情報は登録できました。変更も自由にできますので、適宜調整してください。
またアイテムの数を増減する場合には Size の値を変更してもらうと Element の値がそれに合わせて変化し、追加で登録出来るようになります。

 各数値やエフェクトのプレファブは任意ですが、ItemNo の値だけは異なる番号で設定してください。できれば 0 から連番での採番が理想です。
この番号はアイテムの個体番号として利用する可能性がありますので、同じ番号を重複して設定してしまうと、同じ番号のアイテムが複数存在することになり、
番号によってアイテムを特定することが出来なくなります。


<スクリプタブル・オブジェクトの構造について>


 処理の内容について、順番に確認していきます。

 1.アイテム1つ単位でのデータ(名前、効果値、アイテムのプレファブなどのデータ群)を扱う ItemData クラス
 2.アイテムのデータ(ItemData クラス)をまとめる List(リスト)


1.アイテム1つ単位でのデータ(名前、効果値、アイテムのプレファブなどのデータ群)を扱う ItemData クラス

 Item_RecoveryLife スクリプトにて管理していたアイテムの効果値や、アイテムのプレファブそのものを1つのデータ単位として管理できるように、
ItemDataクラスとして作成して、こちらにて管理を行うようにします。
利点は、1つの ItemData クラス内には1つ分のアイテムの全データが登録できることです。
そのため、ItemData の管理しているアイテムの名前、CharaData の管理している効果値という形で ItemData を参照して利用できる部分です。
例えば、ItemData.itemName と記述すれば、それはその ItemData クラスに登録されている itemName の値を参照することになります。

 ここからはピリオドによる参照処理が増えていきますので、しっかりと処理を読み解いていきましょう

using UnityEngine;

[System.Serializable]
public class ItemData
{
    public string itemName;
    public int itemNo;

    public float efficacyValue;
    public float duration;

    public GameObject effectPrefab;

    public ItemBase itemPrefab;

    // TODO 追加する


}

 このようにアイテム1つに必要になるデータをクラスとしてまとめておくことで管理と利用が容易になります
また、アイテムの情報を増やしたい(使用できる回数や、出現率、属性など)場合には、この ItemData クラス内に 型と変数を追記すれば、好きなだけ増やすことも出来ます。


2.アイテムのデータ(ItemData クラス)をまとめる List(リスト)

 ItemData クラスにはアイテム1つ分に必要な情報をまとめて登録できるようにしました。
このデータはアイテム1つ分ですので、もしも複数のアイテムのデータを用意して登録したい場合、
この ItemData クラスを複数用意して、それを管理するための変数が必要になります。

 大切なのは、ItemData 1つにつき、1つに変数を作っていない部分です。
List 型にして、1つの変数内に複数の ItemData が管理されていることによって、1つのデータ群として利用できるようにしています。
これが重要です。

 こういった1つの同じデータ群をまとまったものをコレクションといいます。
C# にはコレクションを管理する方法として、Dictinary(ディクショナリー)List(リスト) があります。



 List クラスは <T> にジェネリック型(任意の型)を指定して、同じデータ型をまとめて管理するコレクション機能を持つクラスです。
配列と異なり、要素を自由に追加・削除できます。(要素数が可変する)
List はサイズ(長さ)が可変可能な配列のイメージです。

 List を利用する場合には配列と同様に初期化が可能ですが、Listでは初期化時に要素数の宣言が不要です

<配列の初期化>
  ItemData[] itemDatas = new ItemData[3];       // <=  要素数の宣言が必要

<List の初期化>
  List<ItemData> itemDatasList = new List<ItemData>();   // <=  要素数の宣言が不要

 そのため基本的には、予め要素数の確定しているデータを扱う場合には配列を、要素数が未確定であったり可変長であるデータについてはListを利用するように考えてください。


参考サイト
.net column様
【初期化の方法】C#で配列やリストを初期化するには?
https://www.fenet.jp/dotnet/column/language/713/



 public 修飾子にて List を宣言することで、インスペクター上でサイズの変更が可能です
例えばキャラのデータを3体分作って登録したい場合には、インスペクターで List のサイズを 3 に設定すれば
ItemData クラスが 3 つ、Element 0 〜 Element 2 として作成されますので、ここにアイテムのデータを1つずつ、合計3つ分登録することが出来ます。

<ItemData クラスを扱う List>
    public List<ItemData> itemDatasList = new List<ItemData>();



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

 次は アイテムの生成や効果内容にスクリプタブル・オブジェクトを利用する です。