Unity

Custom Property Drawer에서 Drop down menu로 Type 선택하기

YunSeong 2024. 11. 7. 10:42
728x90
반응형

1. Property Drawer이란?

Unity에서 개발을 하다가 보면 Editor에 UI를 커스텀하고 싶을 때가 있다.

그럴 때 사용할 수 있는 것이 Property Drawer이다.
Property Drawer를 상속 받아서 특정 클래스가 Inspector에서 Serialize 되는 방식을 설정할 수 있다.

2. Code

다음은 "Event"란 class를 상속 받는 모든 class를 Reflection으로 받아와서 (private void InitializeClassInfos)

OnGui 에서 Drop Down menu를 정의하여 class 선택지를 주고 

CreateSubClassInstance에서 Reflection으로 그 subclass의 객체를 생성하여 담아준다.

Editor에서 MonoBehaviour Class에서 has-a Class의 Type을 Editor에서 딸깍으로 변경할 수 있게 해준다.

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

[CustomPropertyDrawer(typeof(Event), true)]
public class EventDrawer : PropertyDrawer
{
    //Class Infos
    private static Dictionary<string, Type> classInfos = new Dictionary<string, Type>();

    //Drop Down names
    private static string[] classNames = null;

    //Initializer
    private void InitializeClassInfos()
    {
        Assembly assembly = Assembly.GetAssembly(typeof(Event));
        Type[] types = assembly.GetTypes();
        foreach (Type type in types)
        {
            if (!type.IsAbstract && type.IsSubclassOf(typeof(Event))){
                classInfos.Add(type.Name, type);
            }
        }

        classNames = new string[classInfos.Count];

        int index = 0;
        foreach (string tuple in classInfos.Keys)
        {
            classNames[index] = tuple;
            index++;
        }
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {

        if (classNames == null)
        {
            InitializeClassInfos();
        }

        //initial gui setting
        EditorGUI.BeginProperty(position, label, property);

        // draw default label
        EditorGUI.LabelField(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), label);

        // draw drop down menu
        Rect dropdownRect = new Rect(position.x, position.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUIUtility.singleLineHeight);
        int selectedIndex = GetClassIndex(property);
        int newIndex = EditorGUI.Popup(dropdownRect, "Select Subclass", selectedIndex, classNames);


        // if selected class is change
        if (newIndex != selectedIndex)
        {
            CreateSubclassInstance(property, newIndex);
        }

        // draw selected class
        EditorGUI.PropertyField(new Rect(position.x, position.y + EditorGUIUtility.singleLineHeight * 2, position.width, position.height), property, true);

        //end gui setting
        EditorGUI.EndProperty();
    }

    // get current class index
    private int GetClassIndex(SerializedProperty property)
    {
        if (property.managedReferenceValue == null) return -1;

        string targetName = property.managedReferenceValue.GetType().ToString();
        int index = 0;
        foreach (string name in classNames)
        {
            if (name.Equals(targetName))
            {
                return index;
            }
            index++;
        }

        return -1; //default value
    }

    // set property to instance of selected class
    private void CreateSubclassInstance(SerializedProperty property, int index)
    {
        property.managedReferenceValue = Activator.CreateInstance(classInfos[classNames[index]]);

        property.serializedObject.ApplyModifiedProperties();
    }


    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        // default height
        float height = EditorGUIUtility.singleLineHeight * 3 + EditorGUIUtility.standardVerticalSpacing * 2;

        SerializedProperty iterator = property.Copy();

        // add child properties heights
        while (iterator.NextVisible(true))
        {
            if (iterator.propertyPath.StartsWith(property.propertyPath) && iterator.propertyPath != property.propertyPath)
            {
                height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
            }
            else
            {
                break;
            }
        } 


        return height;
    }
}
728x90
반응형

'Unity' 카테고리의 다른 글

Drag and Drop in Unity  (0) 2023.03.20