본문 바로가기

취미/컴퓨터

Unity - CustomPropertyDrawer 사용하기

CustomPropertyDrawer 만들기

https://gamedev.stackexchange.com/questions/122301/how-can-i-create-a-custom-propertydrawer-for-my-point-struct  에서 가져온 코드로 설명


굳이 CustomPropertyDrawer 사용하는 이유

데이터를 포함하지만 MonoBehaviour에서 파생되지 않는 사용자 지정 개체가있는 경우, 개체를 MonoBehaviour 클래스의 필드로 추가하면 inspector에서 보이지 않는다.

등록할 클래스 (point)

Point.cs

[System.Serializable]
public struct Point
{
    public int X;
    public int Y;
}


Inspector에서 보일 Drawer (point drawer)

PointDrawer.cs:

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(Point))]
public class PointDrawer : PropertyDrawer
{
    SerializedProperty X, Y;
    string name;
    bool cache = false;

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        if (!cache)
        {
            //get the name before it's gone
            name = property.displayName;

            //get the X and Y values
            property.Next(true);
            X = property.Copy();
            property.Next(true);
            Y = property.Copy();

            cache = true;
        }

        Rect contentPosition = EditorGUI.PrefixLabel(position, new GUIContent(name));

        //Check if there is enough space to put the name on the same line (to save space)
        if (position.height > 16f)
        {
            position.height = 16f;
            EditorGUI.indentLevel += 1;
            contentPosition = EditorGUI.IndentedRect(position);
            contentPosition.y += 18f;
        }

        float half = contentPosition.width / 2;
        GUI.skin.label.padding = new RectOffset(3, 3, 6, 6);

        //show the X and Y from the point
        EditorGUIUtility.labelWidth = 14f;
        contentPosition.width *= 0.5f;
        EditorGUI.indentLevel = 0;

        // Begin/end property & change check make each field
        // behave correctly when multi-object editing.
        EditorGUI.BeginProperty(contentPosition, label, X);
        {
            EditorGUI.BeginChangeCheck();
            int newVal = EditorGUI.IntField(contentPosition, new GUIContent("X"), X.intValue);
            if (EditorGUI.EndChangeCheck())
                X.intValue = newVal;
        }
        EditorGUI.EndProperty();

        contentPosition.x += half;

        EditorGUI.BeginProperty(contentPosition, label, Y);
        {
            EditorGUI.BeginChangeCheck();
            int newVal = EditorGUI.IntField(contentPosition, new GUIContent("Y"), Y.intValue);
            if (EditorGUI.EndChangeCheck())
                Y.intValue = newVal;
        }
        EditorGUI.EndProperty();
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return Screen.width < 333 ? (16f + 18f) : 16f;
    }
}

Inspector에서 보일 화면

enter image description here

자세히 뜯어보기

[CustomPropertyDrawer(typeof(Point))]
public class PointDrawer : PropertyDrawer
{
  • 'CustomPropertyDrawer' Attribute를 쓴다
  • 'typeof' 로 어떤 타입에 대한 drawer를 쓸지 알려줘야 한다 (drawer 클래스와 다름)
  • 'Point' 클래스는 Serializable
  • PropertyDrawer를 상속 받아야 한다
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
  • IMGUI(Immediate Mode GUI) 시스템을 사용하기 위해 override 해야하는 함수
  • Update와 마찬가지로 매 프레임마다 실행된다
  • position : GUI가 보여질 위치 / property : 사용자가 지정한 값 / GUIContent : 제목
        EditorGUI.BeginProperty(contentPosition, label, X);
        {
            EditorGUI.BeginChangeCheck();
            int newVal = EditorGUI.IntField(contentPosition, new GUIContent("X"), X.intValue);
            if (EditorGUI.EndChangeCheck())
                X.intValue = newVal;
        }
        EditorGUI.EndProperty();
  • EditorGUI.BeginProperty ([...])로 시작 하고 EditorGUI.EndProperty ()로 끝낸다. 
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return Screen.width < 333 ? (16f + 18f) : 16f;
    }
  • 시스템을 사용하기 위해 override 해야하는 함수
  • 모든 필드는 16 + 2 픽셀 간격으로 높이가 16
  • 두줄이면 16+18 아니면 16 픽셀