【Unity】【エディタ拡張】マテリアルのInspectorをMaterialEditorを使わずに拡張する正しい方法

Unityのエディタ拡張でマテリアルInspectorをMaterialEditorを使わずに拡張する正しい方法についてまとめました。

Unity2018.4.6

ダメな例

今、マテリアルのInspectorを拡張して以下のようなチェックボックスを表示することを考えます。

f:id:halya_11:20190724114956p:plain

OnGUIの引数のmaterialEditorを使えば簡単に描画はできますが、今回は使わない方法を考えます。
そこでコンポーネントのInspectorを拡張する方法と同じような感じで実装してみます。

using UnityEditor;

public class ExampleShaderGUI : ShaderGUI
{
    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
    {
        var exampleProp = FindProperty("_Example", properties);
        exampleProp.floatValue = EditorGUILayout.ToggleLeft(exampleProp.displayName, exampleProp.floatValue == 1.0f) ? 1.0f : 0.0f;
    }
}

表示結果はこんな感じになります。

f:id:halya_11:20190724114956p:plain

一見うまくいったように見えますが、この実装ではマテリアルを複数選択をした時に、
変更を加えていなくても最初に選択したマテリアルのプロパティがすべてに反映されてしまうという不具合が起こります。

正しい方法

マテリアルのプロパティをMaterialEditorを使わずに描画する正しい方法は以下のようになります。

using UnityEditor;

public class ExampleShaderGUI : ShaderGUI
{
    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
    {
        var exampleProp = FindProperty("_Example", properties);

        // 各プロパティに異なる値が設定されていたら専用の見た目に変える
        EditorGUI.showMixedValue = exampleProp.hasMixedValue;

        using (var ccs = new EditorGUI.ChangeCheckScope()) {
            var exampleValue = EditorGUILayout.ToggleLeft(exampleProp.displayName, exampleProp.floatValue == 1.0f) ? 1.0f : 0.0f;
            if (ccs.changed) {
                // 変更があった時のみ反映する
                materialEditor.RegisterPropertyChangeUndo(exampleProp.name);
                exampleProp.floatValue = exampleValue;
            }
        }

        EditorGUI.showMixedValue = false;
    }
}

プロパティを描画するたびに、変更が行われたかどうかをちゃんとチェックしているのがポイントです。

参考

github.com

関連

light11.hatenadiary.com