UnityのScriptableObjectは、設定や定義データをアセットとして管理するために使えます。アイテムの名前や基準価格など、複数のオブジェクトから同じ内容を参照したい場面に向いています。
最初に分けておきたいのは、共通の定義と、プレイヤーごとに変わる状態です。アイテムの基本情報をScriptableObjectに置き、現在の所持数は別のデータで持つ、と考えると使い方を整理できます。
MonoBehaviourとの違い
| 比較 | MonoBehaviour | ScriptableObject |
|---|---|---|
| 今回の置き場所 | GameObjectのコンポーネント | Project内のデータアセット |
| 役割の例 | 画面表示や動作を実行する | アイテムの共通設定を持つ |
| 参照の仕方 | 必要なデータを参照して使う | 複数のコンポーネントから参照される |
ScriptableObject自体をGameObjectにドラッグしてコンポーネントとして追加するわけではありません。定義用スクリプトからアセットを作り、使う側のフィールドへそのアセットを割り当てます。基本の位置づけはUnity公式のScriptableObjectガイドで確認できます。
データの型を作る
ItemDefinition.csというファイルを作り、次のコードを保存します。ファイル名とクラス名をそろえ、Consoleにコンパイルエラーがないことを確認してください。
using UnityEngine;
[CreateAssetMenu(fileName = "NewItem", menuName = "Game Data/Item")]
public class ItemDefinition : ScriptableObject
{
[SerializeField] private string displayName = "新しいアイテム";
[SerializeField] private int basePrice = 100;
public string DisplayName => displayName;
public int BasePrice => basePrice;
}
CreateAssetMenuは、この型のアセットを作るメニューを追加するための属性です。SerializeFieldを付けたフィールドをInspectorで設定し、使う側には読み取り用のプロパティを公開しています。
これはゲーム全体の保存方式を完成させるコードではありません。まず名前と価格という、小さい共通データを作って参照する例です。
Projectにデータアセットを作成する
- Projectウィンドウで保存先のフォルダーを開きます。
- Create → Game Data → Itemからアセットを作ります。
- アセット名をPotionなど、内容の分かる名前にします。
- InspectorでDisplay Nameを「回復薬」、Base Priceを100に設定します。
別のアイテムも必要なら、同じ型から別アセットを作れます。たとえばKeyというアセットを作り、Display Nameを「古い鍵」にすれば、共通の項目構造で異なるデータを管理できます。
MonoBehaviourからアセットを参照する
ItemLabelExample.csを作り、次のコードを保存します。空のGameObjectへ追加して、InspectorのItem欄へ先ほどのPotionアセットを割り当てます。
using UnityEngine;
public class ItemLabelExample : MonoBehaviour
{
[SerializeField] private ItemDefinition item;
private void Start()
{
if (item == null)
{
Debug.LogWarning("Itemにデータアセットを割り当ててください", this);
return;
}
Debug.Log(item.DisplayName + " / 基準価格: " + item.BasePrice, this);
}
}
再生時に「回復薬 / 基準価格: 100」と出れば、アセットを経由して値を読めています。Item欄がNoneなら、スクリプトファイルそのものではなく、作成したデータアセットを割り当ててください。
共有データを書き換えると、参照先全体に影響する
同じアセットを2つのオブジェクトへ割り当てた場合、別々のデータがコピーされるのではなく、同じデータを参照します。片方の処理がそのアセットの値を変えれば、もう片方から見える内容にも影響します。
敵の最大HPを共通の定義として持つのは自然ですが、現在HPまで同じアセットへ保存すると、敵同士で状態を共有してしまう設計になり得ます。最大HPは定義、現在HPは生成した敵ごとのフィールド、と分けて持つ方法を検討します。
今回の例では、通常の利用コードから値を直接代入しにくい形にしています。ただし、Inspectorでの変更や、クラス内部での変更まで禁止する仕組みではありません。
セーブデータの代わりになるとは限らない
ScriptableObjectのアセットを用意しても、ビルドしたゲームで実行中の変更が自動的にアセットファイルへ保存されるわけではありません。ゲーム終了後も所持数や進行状況を残すなら、保存するデータと保存先を別に設計します。
Editor内でのアセット編集と、配布したビルドの実行時動作も区別してください。検証後にデータが変わっていないか、バージョン管理の差分などで確認しておくと、調整用の値が意図せず残るのを見つけやすくなります。
作成メニューや参照欄が出ないとき
- ScriptableObjectを継承しているか。
- CreateAssetMenuを付けているか。
- ファイル名とクラス名をそろえているか。
- プロジェクト内のコンパイルエラーが残っていないか。
- 参照するフィールドがUnityのシリアライズ対象になっているか。
参照と保存の規則はUnity公式のスクリプトシリアライズ解説で確認できます。小さなデータアセットを1つ作り、1つのコンポーネントから読む流れを完成させてから、項目や種類を増やすと管理しやすくなります。
