LoginSignup
2
1

自作の構造体をインスペクター上に表示するかどうかを制御

Last updated at Posted at 2023-06-05

2023/06/05 : 初稿
Unity : 2021.3.15f1

やりたいこと

自作のクラスや構造体をMonoBehaviorのシリアライズパラメータとして持たせるが、
何かフラグなどをみてその内容を表示したりしなかったりしたい。

カスタムエディターを作ればいいけど面倒。

実現方法

カスタムアトリビュートを作成。
表示するかしないかの判定は
MonoBehaviourにinterfaceを継承させてそのメソッドで。

ToggleShowInspector.cs
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Utils
{
    public sealed class ToggleShowInspectorAttribute : PropertyAttribute
    {
        public interface IHandler
        {
            bool ShouldShowInInspector(UnityEngine.Object obj, string propName);
        }
    }

#if UNITY_EDITOR
    [CustomPropertyDrawer(typeof(ToggleShowInspectorAttribute))]
    public sealed class ToggleShowInspectorAttributeDrawer : PropertyDrawer
    {
        // OnGUI
        public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
        {
            // 所有するMonoBehavior取得
            var owner = _property.serializedObject.targetObject;

            // そいつがIHandlerを継承しているならそれを取得
            var handler = owner as ToggleShowInspectorAttribute.IHandler;

            // そいつに表示すべきかどうかを確認
            var shouldShow = (handler != null && handler.ShouldShowInInspector(owner, _property.name));

            // 表示しない
            if (!shouldShow) {
                return;
            }

            // 表示する
            if (_property.hasVisibleChildren) {
                DrawDefaultGUI(_position, _property, _label);
            } else {
                EditorGUI.PropertyField(_position, _property, _label);
            }
        }

        // GetPropertyHeight
        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            return GetDefaultPropertyHeight(property, label);
        }

        // プロパティの描画
        static void DrawDefaultGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            // プロパティ取得
            property = property.serializedObject.FindProperty(property.propertyPath);

            // 表示エリア
            var fieldRect = position;
            fieldRect.height = EditorGUIUtility.singleLineHeight;
            using (new EditorGUI.PropertyScope(fieldRect, label, property)) {
                // ラベル
                if (property.hasVisibleChildren) { // 子要素があれば折り畳み表示
                    property.isExpanded = EditorGUI.Foldout(fieldRect, property.isExpanded, label);
                } else { // 子要素が無ければラベルだけ表示
                    EditorGUI.PropertyField(fieldRect, property);
                    return;
                }
                fieldRect.y += EditorGUIUtility.singleLineHeight;
                fieldRect.y += EditorGUIUtility.standardVerticalSpacing;

                // 展開しているなら中身
                if (property.isExpanded) {
                    using (new EditorGUI.IndentLevelScope()) {
                        // 最初の要素を描画
                        if (!property.NextVisible(true)) {
                            EditorGUI.PropertyField(fieldRect, property, true);
                        } else {
                            var depth = property.depth;
                            EditorGUI.PropertyField(fieldRect, property, true);
                            fieldRect.y += EditorGUI.GetPropertyHeight(property, true);
                            fieldRect.y += EditorGUIUtility.standardVerticalSpacing;

                            // それ以降の要素を描画
                            while (property.NextVisible(false)) {
                                // depthが最初の要素と同じもののみ処理
                                if (property.depth != depth) {
                                    break;
                                }

                                // 要素描画
                                EditorGUI.PropertyField(fieldRect, property, true);
                                fieldRect.y += EditorGUI.GetPropertyHeight(property, true);
                                fieldRect.y += EditorGUIUtility.standardVerticalSpacing;
                            }
                        }
                    }
                }
            }
        }

        // プロパティの高さを取得
        static float GetDefaultPropertyHeight(SerializedProperty property, GUIContent label)
        {
            var height = 0.0f;

            // プロパティ取得
            property = property.serializedObject.FindProperty(property.propertyPath);

            // ラベルの高さ
            height += EditorGUIUtility.singleLineHeight;
            height += EditorGUIUtility.standardVerticalSpacing;

            // 子要素が無い/畳んでいるならラベルだけ
            if (!property.hasVisibleChildren || !property.isExpanded) {
                return height;
            }

            // 最初の要素
            if (property.NextVisible(true)) {
                var depth = property.depth;
                height += EditorGUI.GetPropertyHeight(property, true);
                height += EditorGUIUtility.standardVerticalSpacing;

                // それ以降の要素
                while (property.NextVisible(false)) {
                    // depthが最初の要素と同じもののみ処理
                    if (property.depth != depth) {
                        break;
                    }
                    height += EditorGUI.GetPropertyHeight(property, true);
                    height += EditorGUIUtility.standardVerticalSpacing;
                }

                // 最後はスペース不要なので削除
                height -= EditorGUIUtility.standardVerticalSpacing;
            }

            // 高さを返す
            return height;
        }
    }
#endif
}

使い方はこんな感じ。

Test.cs
using UnityEngine;
using System;
using Utils;

public class Test : MonoBehaviour, ToggleShowInspectorAttribute.IHandler
{
    // トグル
    [SerializeField] bool Toggle;

    // なんかデータ
    [Serializable]
    struct SomethingData
    {
        public string StrData;
    }
    [SerializeField][ToggleShowInspector] SomethingData Data;

    // ToggleShowInspectorAttribute.IHandler
    public bool ShouldShowInInspector(UnityEngine.Object instance, string propName)
    {
        // get instance
        var main = instance as Test;
        if (main == null) {
            return false;
        }

        // Toggleがtrueの時だけDataを表示
        if (propName == "Data") {
            return main.Toggle;
        }

        // 表示しない
        return false;
    }
}
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1