アイテムの名前や値段、敵のHPや攻撃力・・・。こういった「ゲームのデータ」をスクリプトの中に直接書いていくと、数が増えたときに大変ですし、値を少し変えるだけでもスクリプトを開くことになります。今回は、データだけを1つのファイル(アセット)にして、Inspectorで編集出来るようにするScriptableObject(スクリプタブルオブジェクト)をご紹介します!
この記事で学べること
- ScriptableObjectとは何か(MonoBehaviourとの違い)
- ScriptableObjectのクラスを作って、アイテムのデータのアセットを作る方法
- スクリプトからScriptableObjectのデータを使う方法
- セーブデータには「データそのもの」ではなく「ID」を保存する理由
- 再生中にScriptableObjectの値を書き換えるときの注意
前提知識
- 変数でデータを保存しよう
- アセットの作成と管理
- 数が変わるデータを扱おう:List:データの一覧にListを使います
- データを保存しよう:PlayerPrefsとJSON:セーブデータとの関係で使います
ScriptableObjectとは
ScriptableObjectは、データを入れておくためのアセットです。スクリプトで「どんなデータを持つか」を決めておくと、Projectウインドで右クリックして、そのデータのファイルをいくつでも作れます。
| MonoBehaviour | ScriptableObject | |
|---|---|---|
| どこにある? | シーンのGameObjectに付ける | Projectウインドのアセット(ファイル) |
| 主な役割 | 動き・処理(Update、当たり判定など) | データ(名前、値段、HPなど) |
| いくつ作る? | 付けたGameObjectの数だけ | アセットの数だけ(1つのアセットを、いろんなところから使える) |
| 例 | プレイヤーの移動、ボタンの処理 | アイテムの一覧、敵の強さ、ステージの設定 |
「アイテム1つ1つをファイルにして、Inspectorで値を入れる」イメージです。
ScriptableObjectのクラスを作る
アイテムのデータを持つScriptableObjectを作ってみましょう。
- Projectウインドで右クリック>Create>MonoBehaviour Script で「ItemData」を作り、中身を次のように書き換える
using UnityEngine;
// アイテム1つ分のデータ。Project ウインドの Create>Game>Item Data でアセットを作れる
[CreateAssetMenu(fileName = "NewItem", menuName = "Game/Item Data")]
public class ItemData : ScriptableObject
{
public string id; // セーブデータに保存する名前(英数字で、ほかのアイテムと重ならないように)
public string itemName; // 画面に出す名前
public Sprite icon; // アイコン
public int price; // 値段
[TextArea] public string description; // 説明文
}
- : ScriptableObject
- MonoBehaviourの代わりに、ScriptableObjectを継承する
- Update・Startなどは使わない。データを入れておくだけのクラス
- [CreateAssetMenu]
- Projectウインドの右クリック>Createのメニューに、このアセットを作る項目が出る
- fileName:作ったときの最初のファイル名
- menuName:メニューのどこに出すか(「Game/Item Data」なら、Create>Game>Item Data)
- [TextArea]
- Inspectorで、何行も書ける大きな入力欄になる
- ファイル名とクラス名をそろえる
- ItemData.csの中はItemDataクラスにする。違うと、アセットが作れない
アセットを作る
- Projectウインドで右クリック>Create>Game>Item Data
- 名前を「Potion」にする
- Inspectorで値を入れる
- Id:potion
- Item Name:やくそう
- Icon:好きなSprite
- Price:10
- Description:HPを少し回復する
- 同じように「Antidote」(どくけしそう)や「Bomb」(ばくだん)も作ってみる
アイテムを増やしたいときは、スクリプトを触らずにアセットを増やすだけでOK。値段を変えたいときも、Inspectorで数字を変えるだけです。
スクリプトからデータを使う
作ったアセットは、[SerializeField]の変数にドラッグ&ドロップして使います。
using System.Collections.Generic;
using UnityEngine;
// ショップの棚。並べるアイテムのデータを Inspector で設定して、Console に一覧を出す
public class ItemShelf : MonoBehaviour
{
[SerializeField] private List<ItemData> items = new List<ItemData>();
private void Start()
{
foreach (ItemData item in items)
{
Debug.Log($"{item.itemName}:{item.price}コイン({item.description})");
}
}
}
- 空のGameObjectにItemShelfを付けて、InspectorのItemsの「+」で欄を増やし、作ったアセットをドラッグ&ドロップする
- 再生すると、Consoleにアイテムの名前と値段が並ぶ
- List
- Listについては6-13 数が変わるデータを扱おう:Listで紹介しています
- 同じアセットを、いろんなところから使える
- ショップの棚でも、持ち物の画面でも、同じPotionのアセットを参照すればOK。値段を変えると、全部に反映される
セーブデータには「ID」を保存する
ScriptableObjectのデータを、そのままJsonUtilityで保存しようとすると、うまくいきません。
- JsonUtilityでScriptableObjectの変数を保存すると、中身ではなく「そのときの番号(インスタンスID)」が保存される
- インスタンスIDは、Unityを起動し直すと変わってしまう。次に読み込んだとき、どのアイテムか分からなくなる
そこで、セーブデータにはアイテムのID(文字)を保存して、読み込むときにIDからアイテムのデータを探します。ItemDataにidを用意したのはそのためです。
全部のアイテムを登録しておく「図鑑」のようなScriptableObjectを作っておくと、IDから探すのが簡単になります。
using System.Collections.Generic;
using UnityEngine;
// ゲームに出てくるアイテムを全部登録しておく図鑑。ID からアイテムのデータを探せる
[CreateAssetMenu(fileName = "ItemDatabase", menuName = "Game/Item Database")]
public class ItemDatabase : ScriptableObject
{
[SerializeField] private List<ItemData> items = new List<ItemData>();
public IReadOnlyList<ItemData> Items => items;
// ID が同じアイテムを探す(なければ null)
public ItemData FindById(string id)
{
foreach (ItemData item in items)
{
if (item.id == id)
{
return item;
}
}
return null;
}
}
- 保存するとき:
item.id(”potion”など)を保存する - 読み込むとき:
database.FindById("potion")で、Potionのアセットが返ってくる - IReadOnlyList
- ほかのスクリプトからは一覧を読むだけで、足したり消したり出来ないようにしている
スライムIDはあとから変えると、保存したデータが読めなくなります・・・一度決めたら変えないようにしましょう
再生中に値を書き換えるときの注意
ScriptableObjectはアセット(ファイル)なので、再生中にスクリプトで値を書き換えると、エディタでは再生を止めても元に戻りません。
- エディタで遊んでいるときに
item.price = 0;のように書き換えると、Potionのアセットそのものが0になってしまう - ビルドしたゲームでは、アプリを終了すると元に戻る(ファイルには保存されない)
なので、ScriptableObjectには変わらないデータ(名前、値段、最大HPなど)だけを入れて、変わるデータ(持っている数、今のHPなど)は、MonoBehaviourやセーブデータの方に持たせるのがオススメです。
| ScriptableObjectに入れる(変わらない) | 別に持つ(変わる) |
|---|---|
| アイテムの名前・値段・アイコン | 持っている数 |
| 敵の最大HP・攻撃力 | 戦闘中の今のHP |
| ステージの制限時間 | 残り時間 |



ScriptableObjectは「設計図のデータ」、変わる値は「セーブデータ」。分けておけば安心だね!
よくあるエラーと対処法
エラー:Create>Game>Item Dataがメニューに出てこない
- 原因:スクリプトにコンパイルエラーがある、または[CreateAssetMenu]を付け忘れている
- 対処法:Consoleの赤いエラーを直す。[CreateAssetMenu]がクラスのすぐ上にあるか確認する
警告:The associated script can not be loaded.(アセットのInspectorに出る)
- 原因:ファイル名とクラス名が違う、またはスクリプトの名前を変えた
- 対処法:ItemData.csの中のクラス名をItemDataにそろえる
エラー:NullReferenceException
- 原因:[SerializeField]の変数にアセットを入れていない。または
FindByIdで見つからずnullが返った - 対処法:Inspectorで設定されているか確認する。IDのつづり(大文字・小文字)がアセットと同じか確認する
エラー:再生を止めても値が元に戻らない
- 原因:再生中にスクリプトでScriptableObjectの値を書き換えた
- 対処法:変わる値は、ScriptableObjectではなくMonoBehaviourやセーブデータに持たせる
まとめ
- ScriptableObjectはデータを入れておくアセット。MonoBehaviourの代わりにScriptableObjectを継承する
- [CreateAssetMenu]を付けると、Projectウインドの右クリックでアセットを作れる
- アセットは[SerializeField]の変数にドラッグ&ドロップして使う。同じアセットをいろんなところから使える
- セーブデータには、データそのものではなくIDを保存して、読み込むときにIDから探す
- 再生中に書き換えた値は、エディタでは元に戻らない。変わる値は別に持つ
ScriptableObjectの使い方をもっと知りたい人は、ScriptableObjectの基礎!シーン間でデータ受け渡しなども参考にしてください。
次のステップ
- ScriptableObjectでアイテムのデータを作り、ショップに並べる回:ペット育成ゲームの作り方#7
- Unityスタートガイド 目次
コメント