ScriptableObjectの実行時変更が保存されてしまう問題と安全な対策方法

1. 問題の概要

ScriptableObjectはUnityのアセットとして保存されるデータオブジェクトですが、

実行中にこのアセットを直接変更すると、変更内容がプロジェクトに永続化されてしまうことがあります。

これは、一時的なデバッグやテストのつもりで変更した値が、次回以降も保持されてしまうという不具合につながります。


2. 典型的な問題例

[SerializeField] private MyData configData;

void Start()
{
    configData.hp = 9999; // ← アセット自体を書き換えてしまう危険なコード
}

このクラスは、例で使用した hp プロパティを持ち、Unityエディタからインスペクター上で編集できるシンプルな構成です。


MyData.cs

using UnityEngine;

[CreateAssetMenu(fileName = "NewMyData", menuName = "ScriptableObjects/MyData")]
public class MyData : ScriptableObject
{
    [Header("基本ステータス")]
    public int hp = 100;
    public int attack = 10;
    public int defense = 5;
}

作成手順(Unityエディタでの操作)

  1. 上記スクリプトを Assets/Scripts/ フォルダなどに保存する。
  2. Unityエディタの Projectビューで右クリック →Create > ScriptableObjects > MyData を選択。
  3. 任意の名前(例:PlayerData, EnemyData など)でアセットを作成。
  4. 作成したアセットをインスペクターで開き、hp, attack, defense を編集。
  5. 実行時には Instantiate() でこのアセットを複製して使用する。

メモ

  • [CreateAssetMenu] 属性を使うことで、Unityエディタから簡単にインスタンスを作成できます。
  • この MyData はプレイヤーや敵キャラ、スキル、アイテムなど幅広く応用可能です。

このようなコードでは、アセットに保存されている値 hp が、実行後も9999のまま残ってしまう場合があります。


3. 安全な対策:

Instantiate()によるクローンの作成

✅ 推奨される実装方法

[SerializeField] private MyData originalData;

private MyData runtimeData;

void Start()
{
    // アセットの複製(実行時限定のクローン)
    runtimeData = Instantiate(originalData);

    // runtimeDataを操作しても、元のアセットには影響しない
    runtimeData.hp = 9999;
}

✅ こうすることで:

  • 元の originalData アセットは安全に保たれる
  • runtimeData は実行中だけ使われ、変更は保存されない
  • デバッグ時も安心して操作・変更ができる

4. まとめ

  • ScriptableObjectを直接編集すると、変更がUnityエディタに保存される危険がある
  • 実行時のデバッグには、必ず Instantiate() で複製して操作することが安全
  • これにより、元のアセットを汚染せずに安心してテスト・変更が行える

補足:ScriptableObjectの用途

  • ゲームの設定情報(例:敵キャラのステータス、ゲームバランス値など)
  • スキルやアイテム定義など、共通データのテンプレートとして非常に有効

→ だからこそ、アセットを誤って汚すと全体に影響するため注意が必要です。

ScriptableObject,Unity

Posted by hidepon