げぇむぷろぐらみんぐ

日々の生活で得た知識、経験を書きます

【Unity】ScriptableObjectを作成するエディタ拡張

はじめに

色々なパラメータをアセットデータとして保持することができるScriptableObjectですが、一般的に以下のようなクラスを作成して作ります。

using UnityEngine;
using UnityEditor;

[CreateAssetMenu(menuName = "ScriptableObject/ExampleAsset")]
public class ExampleAsset : ScriptableObject
{
}

このように書くと、以下のようなMenuが追加されExampleAssetの作成が可能となります。

f:id:siguma_sig:20180617165745p:plain

しかし、開発が進むにつれて作成したいScriptableObjectの数が増えてくるとともに、Menuに追加される項目も増えます。 丁寧に階層分けをするようにパスを記述しても良いですが、めんどくさいので今回はScriptableObjectを渡すと任意の場所にアセットを作成してくれるエディタ拡張を書いたので、コードを載せておきます。

作ったもの

以下のようなウィンドウで、ScriptableObjectを作成します。 TargetScriptにアセットを作りたいScriptableObjectを渡して上げることで簡単にアセットを作成できます。 ScriptableObjectのソースファイル側にはパス等を記述する必要がなくなるので、管理がしやすくなると思います。

f:id:siguma_sig:20180617170230p:plain

コード

以下がソースコードです。

using UnityEditor;
using UnityEngine;
using System.IO;

public class ScriptableObjectCreator : ScriptableWizard {

    [SerializeField]
    private MonoScript _targetScript;
    [SerializeField]
    private string _outputObjectName;
    [SerializeField]
    private string _outputDirectory;

    private static readonly string _outputPrefix = "Generated";
    private static readonly string _outputSuffix = ".asset";

    private MonoScript _targetCache = null;

    [MenuItem("ScriptableObject/Creator")]
    private static void Open() {
        DisplayWizard<ScriptableObjectCreator>("Scriptable Object Creator");
    }

    private void OnWizardCreate() {
        SafeCreateDirectory(_outputDirectory);
        var createdObject = CreateInstance(_targetScript.name);
        var outputPath = Path.Combine(_outputDirectory, _outputObjectName + _outputSuffix);
        var uniqueOutputPath = AssetDatabase.GenerateUniqueAssetPath(outputPath);
        AssetDatabase.CreateAsset(createdObject, uniqueOutputPath);
        AssetDatabase.Refresh();
    }

    private void OnWizardUpdate() {
        if (_targetScript != null && _targetCache != _targetScript) {
            _outputObjectName = _outputPrefix + _targetScript.name;
            _outputDirectory = Path.GetDirectoryName(AssetDatabase.GetAssetPath(_targetScript));
            _targetCache = _targetScript;
        }
        isValid = _targetScript != null && !string.IsNullOrEmpty(_outputObjectName) && !string.IsNullOrEmpty(_outputDirectory);
    }

    private void SafeCreateDirectory(string path) {
        var currentPath = "";
        var splitChar = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };

        foreach (var dir in path.Split(splitChar)) {
            var parent = currentPath;
            currentPath = Path.Combine(currentPath, dir);
            if (!AssetDatabase.IsValidFolder(currentPath)) {
                AssetDatabase.CreateFolder(parent, dir);
            }
        }
    }
}

コードの説明

今回は、アセットを作成するので楽をするためにScriptableWizardを使ってみました。
結果的に、1個作るたびにウィンドウが勝手に閉じて面倒なので、普通にEditorWindowにしとけばよかったと思いました…

ポイントは、ScriptableObjectアセットを作成する元のスクリプトを受け取るためにMonoScriptを変数として定義しておくことくらいです。

あとは、ファイル名やパスは基本的にはデフォルト値がセットされるようになっているのと、パスに存在しないディレクトリがあったら作成してから処理をするようにしているだけです。

参考

【Unity】【エディタ拡張】スクリプトからスクリプトファイル(.cs)を生成する - LIGHT11