Unity 엔진 내장 에디터툴에서 자주 사용되는 폴드바 GUI 에디터 스크립트입니다.

EditorGUILayout과 같은 기본 에디터 클래스에는 구현되어 있지 않아 직접 제작해야합니다.

 

GUI 클래스

public static class CategoryHeader
{
    protected static class Styles
    {
        public static readonly GUIStyle foldoutStyle;
        public static readonly GUIStyle categoryHeader;

        static Styles()
        {
            var builtInSkin = GetCurrentSkin();
            foldoutStyle = new GUIStyle(EditorStyles.foldout);
            foldoutStyle.fontStyle = FontStyle.Bold;

            categoryHeader = new GUIStyle(builtInSkin.label);
            categoryHeader.fontStyle = FontStyle.Bold;
            categoryHeader.border.left = 2;
            categoryHeader.padding.left = 32;
            categoryHeader.padding.top = 2;
            categoryHeader.border.right = 2;
  
            categoryHeader.normal.background = GetBackgroundProcedural_Twotone(new Color(0.196f, 0.196f, 0.196f),
                                                                                new Color(0.121f, 0.121f, 0.121f));
        }

        private static Texture2D GetBackgroundProcedural(Color color)
        {
            Texture2D texture = new Texture2D(1, 1);
            texture.SetPixel(0, 0, color);
            texture.Apply();
            return texture;
        }

        private static Texture2D GetBackgroundProcedural_Twotone(Color color, Color headerColor)
        {
            const int height = 16;
            Texture2D texture = new Texture2D(1, height);
            for(int i=0; i< height - 1; i++)
            {
                texture.SetPixel(0, i, color);
            }
            texture.SetPixel(0, height - 1, headerColor);
            texture.Apply();
            return texture;
        }

        private static Texture2D GetBackgroundAsset(string assetPath)
        {
            Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
            if (!texture)
            {
                Debug.LogWarning("GUITexture Asset does not exist : " + assetPath);
                texture = null;
            }
            return texture;
        }

        public static GUISkin GetCurrentSkin()
        {
            return EditorGUIUtility.isProSkin ? EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene) : EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);
        }
    }

    public static bool ShowHeader(GUIContent nameContent, bool foldoutState, string preferenceName = null)
    {
        float height = Styles.categoryHeader.CalcHeight(nameContent, 4000) + 3;

        Rect rect = GUILayoutUtility.GetRect(1, height - 1);
        rect.width += rect.x;
        rect.x = 0;

        if (Event.current.type == EventType.Repaint)
            Styles.categoryHeader.Draw(rect, nameContent, false, true, true, false);

        bool result = false;
        rect.x += 14;
        rect.width -= 2;
        result = EditorGUI.Toggle(rect, foldoutState, Styles.foldoutStyle);

        EditorGUI.indentLevel = result ? 1 : 0;

        if (preferenceName != null && result != foldoutState)
        {
            EditorPrefs.SetBool(preferenceName, result);
        }

        return result;
    }

    public static bool ShowHeader(string label, bool foldoutState, string preferenceName = null)
    {
        return ShowHeader(EditorGUIUtility.TrTempContent(label), foldoutState, preferenceName);
    }
}

 

 

 

사용 샘플 & 전체 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;


[CustomEditor(typeof(CustomInspectorSample))]
public class CustomInspectorSample_Editor : Editor
{
    protected struct PropertyDefine
    {
        public SerializedProperty prop;
        public string path;

        public PropertyDefine(string path)
        {
            this.prop = null;
            this.path = path;
        }

        public void Init(SerializedObject serializedObject)
        {
            prop = serializedObject.FindProperty(path);
        }
    }

    protected static class Contents
    {
        public static readonly GUIContent headerGeneral = EditorGUIUtility.TrTextContent("General");
        public static readonly GUIContent amount = EditorGUIUtility.TrTextContent("Amount", "Amount Property Description");
    }

    const string kGeneralFoldoutStatePreferenceName = "CustomInspectorSample_Editor.Foldout.General";
    private bool showGeneralCategory;
    private bool showRendererCategory;

    private PropertyDefine _amountProp = new PropertyDefine("_amount");
    private PropertyDefine _colorProp = new PropertyDefine("_color");

    private CustomInspectorSample _component;
    

    private void OnEnable()
    {
        _component = (CustomInspectorSample)target;
        showGeneralCategory = EditorPrefs.GetBool(kGeneralFoldoutStatePreferenceName, true);
        
        _amountProp.Init(serializedObject);
        _colorProp.Init(serializedObject);
    }

   
    public override void OnInspectorGUI()
    { 
        if (!_component) return;
        serializedObject.Update();

        EditorGUI.BeginChangeCheck();
        GUILayout.Space(6);

        showGeneralCategory = CategoryHeader.ShowHeader(Contents.headerGeneral, showGeneralCategory, kGeneralFoldoutStatePreferenceName);
        if(showGeneralCategory)
        {
            EditorGUILayout.PropertyField(_amountProp.prop, Contents.amount);
            GUILayout.Space(6);
        }
        
        showRendererCategory = CategoryHeader.ShowHeader("Renderer", showRendererCategory);
        if (showRendererCategory)
        {
            EditorGUILayout.PropertyField(_colorProp.prop);
            GUILayout.Space(6);
        }

        if (EditorGUI.EndChangeCheck())
        {
            serializedObject.ApplyModifiedProperties();
        }   
    }
}

public static class CategoryHeader
{
    protected static class Styles
    {
        public static readonly GUIStyle foldoutStyle;
        public static readonly GUIStyle categoryHeader;

        static Styles()
        {
            var builtInSkin = GetCurrentSkin();
            foldoutStyle = new GUIStyle(EditorStyles.foldout);
            foldoutStyle.fontStyle = FontStyle.Bold;

            categoryHeader = new GUIStyle(builtInSkin.label);
            categoryHeader.fontStyle = FontStyle.Bold;
            categoryHeader.border.left = 2;
            categoryHeader.padding.left = 32;
            categoryHeader.padding.top = 2;
            categoryHeader.border.right = 2;
  
            categoryHeader.normal.background = GetBackgroundProcedural_Twotone(new Color(0.196f, 0.196f, 0.196f),
                                                                                new Color(0.121f, 0.121f, 0.121f));
        }

        private static Texture2D GetBackgroundProcedural(Color color)
        {
            Texture2D texture = new Texture2D(1, 1);
            texture.SetPixel(0, 0, color);
            texture.Apply();
            return texture;
        }

        private static Texture2D GetBackgroundProcedural_Twotone(Color color, Color headerColor)
        {
            const int height = 16;
            Texture2D texture = new Texture2D(1, height);
            for(int i=0; i< height - 1; i++)
            {
                texture.SetPixel(0, i, color);
            }
            texture.SetPixel(0, height - 1, headerColor);
            texture.Apply();
            return texture;
        }

        private static Texture2D GetBackgroundAsset(string assetPath)
        {
            Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
            if (!texture)
            {
                Debug.LogWarning("GUITexture Asset does not exist : " + assetPath);
                texture = null;
            }
            return texture;
        }

        public static GUISkin GetCurrentSkin()
        {
            return EditorGUIUtility.isProSkin ? EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene) : EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);
        }
    }

    public static bool ShowHeader(GUIContent nameContent, bool foldoutState, string preferenceName = null)
    {
        float height = Styles.categoryHeader.CalcHeight(nameContent, 4000) + 3;

        Rect rect = GUILayoutUtility.GetRect(1, height - 1);
        rect.width += rect.x;
        rect.x = 0;

        if (Event.current.type == EventType.Repaint)
            Styles.categoryHeader.Draw(rect, nameContent, false, true, true, false);

        bool result = false;
        rect.x += 14;
        rect.width -= 2;
        result = EditorGUI.Toggle(rect, foldoutState, Styles.foldoutStyle);

        EditorGUI.indentLevel = result ? 1 : 0;

        if (preferenceName != null && result != foldoutState)
        {
            EditorPrefs.SetBool(preferenceName, result);
        }

        return result;
    }

    public static bool ShowHeader(string label, bool foldoutState, string preferenceName = null)
    {
        return ShowHeader(EditorGUIUtility.TrTempContent(label), foldoutState, preferenceName);
    }
}

 


WRITTEN BY
CatDarkGame
Technical Artist dhwlgn12@gmail.com

,