【Unity】ScriptableObjectの使い方と落とし穴【コピペ可】

に公開
使い方と落とし穴シリーズ一覧
さっさと試したい人向け コピペ可コード
  • PlayerStatus.cs (ScriptableObject側 エディタ上で生成する側)
using UnityEngine;

[CreateAssetMenu(menuName = "Status/PlayerStatus")]
public class PlayerStatus : ScriptableObject
{
    [SerializeField] private float hp = 100f;
    public float HP => hp;
}
  • Player.cs (ScriptableObjectを利用する側)
using UnityEngine;

public class Player : MonoBehaviour
{
    [SerializeField] private PlayerStatus status;

    private void Start()
    {
        Debug.Log($"初期HP: {status.HP}");
    }
}

使い方

1:Assetsフォルダ内で PlayerStatus.cs を作成
2:Player.cs をシーン上のオブジェクトにアタッチ
3:プロジェクトビュー → Create → Status → PlayerStatus でアセット作成
4:作成アセットを Playerstatus にドラッグ

一言

  • ScriptableObject の習得で Unity への抵抗感がグッとなくなる (経験談)
  • 開発に必須…ではないが、Unity のディープな機能を色々扱っていく上で
    最初の良いステップと考えています

ScriptableObjectを使用するケース

  • 定数の管理
  • キャラクタークラスごとの初期ステータス
  • シーン間で共通する設定情報

ScriptableObjectの使い方

全体の流れ

ScriptableObject 継承

[CreateAssetMenu(menuName = "Status/PlayerStatus")]
public class PlayerStatus : ScriptableObject
{
    [SerializeField] private float hp;
    public float HP => hp;

    //他なんかデータ色々
}
  • [CreateAssetMenu(menuName = "任意の名前")] を追加
  • MonoBehaviourScriptableObject に変更
  • クラス内には後々エディタ上で操作したいデータを記述(例:hp)

エディタ上で生成

  • エディタ上で右クリック → Create → Status → PlayerStatus を選択するとプロジェクトビューに PlayerStatus ファイルが作成される
  • [SerializeField], public な変数はインスペクターから操作できる

他スクリプトで使用

public class Player : MonoBehaviour
{
    [SerializeField] private PlayerStatus status;

    private void Start()
    {
        Debug.Log($"初期HP:{status.HP}");
    }
}
  • ScriptableObject(PlayerStatus) を参照して使うことができる

ScriptableObjectのメリット

  • エディタ上で操作できるデータ群を作りやすい
  • 複数シーンやゲーム全体にまたがったデータ群を作りやすい
  • 静的で共用のデータに向いている(例:敵のHPや移動速度等の固定パラメータ)
    • 全インスタンスが値を持つより、ScriptableObject 参照の方がメモリ使用量が抑えられる
    • Flyweightパターン実装に活用することもできる

オイラはこんな落とし穴に出会った

データ書き換えで想定外のやつも変わる

  • 複数スクリプトで使用する場合に注意(そう珍しくない状況)
  • スクリプトは ScriptableObject参照する
  • そのため、「スクリプトA調整するか…」とうっかり参照元を変え、他にも影響が出てしまうケースがある

new() で生成

  • ScriptableObject継承クラスを new() すると警告がでる(UNT0011)
  • この場合 Unity message methods(OnEnable() とか)が正しく動作しない可能性がある
  • そのため、CreateInstance() を代わりに用いる
var status = new PlayerStatus(); //UNT0011
var status = ScriptableObject.CreateInstance<PlayerStatus>(); //OK
  • なお、これで作成した ScriptableObject はメモリ上にのみ存在するため、アセットとして保存したい場合は別途 AssetDatabase.CreateAsset()が必要

実行中に書き換え → そのまま

  • エディタで実行中、ScriptableObject のデータを書き換えても終了後そのまま
  • これはむしろ便利だが、Monobehaviour との挙動とは異なるので注意(終了後、元に戻る)
  • 筆者はその辺を勘違いしてパラメータで遊んでたら盛大にバグった経験アリ(99%筆者が悪い)

参考

Discussion