[Unity] ScriptableObjectを使ってModelを作る

  • 16
    Like
  • 0
    Comment
More than 1 year has passed since last update.

ScriptableObject なるものを最近知りました。
PrefabのようにAssetsとして保存され、自身で定義した処理を実行することができる、まさに「スクリプタブル」なオブジェクトです。

これを使うことで、いわゆるMVCパターンのモデルに相当するものが作れそうです。
利点としてはシーンをまたいでも値が保持される、という点。(起動時に値が設定される模様)
(ちなみにUnityエディタ上ではPlayボタンを押してストップしても値は保持されるみたい。つまりそれを参照しているプロセスが存在している状態なら値は生きていそう)

ScriptableObjectを継承したクラスを作る

まずはこのオブジェクトの処理部分を定義します。そのために ScriptableObject を継承したクラスを作ります。

using UnityEngine;

public class SampleModel : ScriptableObject {

    public delegate void ChangePointHandler(int point);
    public event ChangePointHandler OnChangePoint;


    public int Point { get; set; }

    void OnEnable() {
        Debug.Log("////////// Initialize model //////////");
        Point = 0;
    }

    public void AddPoint(int point) {
        Point += point;
        if (OnChangePoint != null) {
            OnChangePoint(Point);
        }
    }

    public void ShowPoint() {
        Debug.Log(Point);
    }
}

ちなみに、初期化処理は OnEnable に書いておくとUnityエディタ上でも意図した状態でスタートできるのでいいと思います。
(Playボタン押下時に毎回初期化処理が呼ばれるが、シーンをロードした場合は呼ばれない)

ただ、このままではオブジェクトが生成されないので、Assetsに保存するための処理が必要になります。
毎回処理するのはめんどくさいのでEditorスクリプトを書いておくといいと思います。
ScriptableObjectの使い方 | テラシュールブログに説明と、生成するスクリプトの紹介があるので参考にするといいと思います。

また、データが更新された際にそれをイベントとして通知するため、delegateevent を定義しておきます。
引数は変化後のポイントです。

ScriptableObjectを生成・保存する

上記で書いた通り、定義した ScriptableObject をAssetsに保存しないとなりません。
保存するには以下のようにします。

using UnityEngine;
using UnityEditor;

[InitializeOnLoad]
public class CreateScriptAsset {
    static CreateScriptAsset() {
        // SampleModelを生成
        SampleModel model = ScriptableObject.CreateInstance<SampleModel>();

        // ユニークなパスを生成する
        // (多分、重複する名前がある場合、自動的に(2)とかつけてくれるやつ)
        string path = AssetDatabase.GenerateUniqueAssetPath("Assets/Model/" + typeof(SampleModel) + ".asset");
        Debug.Log("Path: " + path);

        // 該当パスにオブジェクトアセットを生成
        AssetDatabase.CreateAsset(model, path);

        // 未保存のアセットをアセットデータベースに保存
        AssetDatabase.SaveAssets();
    }
}

ちなみに、上記スクリプトを何度も走らせるとその分だけAssetsにファイルが追加されるので、保存したら消すか、Editorスクリプトにして任意のタイミングで実行できるようにしておくといいでしょう。
(上のテラシュールブログで紹介されているような方法)

ScriptableObjectを使う

最後に、上記で生成したオブジェクトを使用する方法です。

using UnityEngine;
using System.Collections;

public class ModelGetter : MonoBehaviour {

    public SampleModel model;

    void Start() {
        model.OnChangePoint += OnChange;
    }

    // Update is called once per frame
    void Update () {
        if (Input.GetMouseButtonDown(0)) {
            model.AddPoint(1);
            // model.ShowPoint();

            if (model.Point > 10) {
                model.Point = 5;
                Application.LoadLevel("Sub");
            }
        }
    }

    void OnChange(int point) {
        Debug.Log("Event was receirved: " + point);
    }
}

上記スクリプトをGameObjectにアタッチし、さらに public で宣言されている SampleModel 型の変数にエディタから、上記「 ScriptableObjectを生成・保存する 」で生成したオブジェクトを設定しておきます。

cap.png

このオブジェクトの変数に保持される値は、ゲーム実行中は保持され続けるので(別シーンをロードしても)、まさにモデルとして利用できるものになります。