🎓 Unity CustomEditor TIL (Today I Learned)

2025. 5. 14. 20:38[Unity] Game Programing

Unity CustomEditor 고급 TIL

날짜: 2025년 5월 14일

주제: Unity에서 CustomEditor를 사용하여 사용자 정의 인스펙터를 구현하는 법, 그 작동 원리, 확장 및 최적화 기법에 대해 학습하고 정리하였다.


1. CustomEditor란 무엇인가?

CustomEditor는 Unity의 Editor 스크립팅 기능 중 하나로, 컴포넌트 또는 ScriptableObject의 인스펙터(Inspector) UI를 커스터마이징 할 수 있게 해주는 클래스이다. 일반적으로 MonoBehaviour나 ScriptableObject를 상속받은 클래스 위에 작동한다.

1.1 기본 구조

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyComponent))]
public class MyComponentEditor : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector(); // 기본 인스펙터를 그대로 출력
        // 커스텀 GUI 작성 가능
    }
}

CustomEditorEditor를 상속하며, 타겟 오브젝트에 대한 UI를 재정의할 수 있다.

1.2 타겟 지정

[CustomEditor(typeof(MyComponent))]는 이 에디터가 MyComponent 클래스에 대응된다는 의미이다.


2. 주요 메서드 및 속성

  • target: 현재 편집 중인 대상 오브젝트. 형변환하여 사용해야 함.
  • serializedObject: 타겟 오브젝트를 감싼 SerializedObject. Undo, 멀티-오브젝트 지원을 위해 중요.
  • OnInspectorGUI(): 인스펙터에 그려지는 메인 UI. 대부분의 작업은 이 메서드에서 이루어짐.
  • EditorGUILayout: 자동 레이아웃 기반 UI 구성 요소
  • EditorGUI: 절대 위치 기반 UI 구성 요소

예제: 커스텀 필드 추가

public override void OnInspectorGUI()
{
    MyComponent comp = (MyComponent)target;

    comp.speed = EditorGUILayout.FloatField("Speed", comp.speed);

    if (GUILayout.Button("Reset Speed"))
    {
        comp.speed = 0;
    }

    if (GUI.changed)
    {
        EditorUtility.SetDirty(comp);
    }
}

3. SerializedProperty 사용하기

SerializedProperty는 Unity 내부의 직렬화 시스템을 직접 다룰 수 있게 해준다. 멀티 오브젝트 편집, Undo, Prefab 지원 등을 위해 필수적으로 사용해야 한다.

3.1 예시

SerializedProperty speedProp;

void OnEnable()
{
    speedProp = serializedObject.FindProperty("speed");
}

public override void OnInspectorGUI()
{
    serializedObject.Update();

    EditorGUILayout.PropertyField(speedProp);

    serializedObject.ApplyModifiedProperties();
}

주의: serializedObject.ApplyModifiedProperties()를 호출하지 않으면 값이 반영되지 않는다.


4. 고급 기능

4.1 멀티 오브젝트 편집 지원

[CanEditMultipleObjects] 어트리뷰트를 사용하면 여러 오브젝트 선택 시에도 에디터가 작동한다.

[CustomEditor(typeof(MyComponent))]
[CanEditMultipleObjects]
public class MyComponentEditor : Editor { ... }

4.2 Foldout, Tabs, Tooltip 구현

bool showAdvanced = false;

public override void OnInspectorGUI()
{
    showAdvanced = EditorGUILayout.Foldout(showAdvanced, "Advanced Settings");
    if (showAdvanced)
    {
        EditorGUILayout.PropertyField(serializedObject.FindProperty("someValue"));
    }
}

4.3 버튼 스타일

GUIStyle boldStyle = new GUIStyle(GUI.skin.button);
boldStyle.fontStyle = FontStyle.Bold;

if (GUILayout.Button("Apply Settings", boldStyle))
{
    // 실행 코드
}

5. ScriptableObject용 CustomEditor

ScriptableObject도 MonoBehaviour처럼 CustomEditor를 통해 커스터마이징할 수 있다. 다만 생성은 일반적으로 에셋 형태로 이루어진다.

에셋 자동 생성 예시

[CreateAssetMenu(fileName = "MySettings", menuName = "My Game/Settings")]
public class GameSettings : ScriptableObject
{
    public float gravity;
    public float speed;
}

커스텀 에디터

[CustomEditor(typeof(GameSettings))]
public class GameSettingsEditor : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        GameSettings settings = (GameSettings)target;

        if (GUILayout.Button("Reset"))
        {
            settings.gravity = 9.81f;
            settings.speed = 5f;
        }
    }
}

6. PropertyDrawer로 필드 단위 커스터마이징

CustomEditor는 클래스 단위 UI 커스터마이징, PropertyDrawer는 필드 단위 커스터마이징이다.

예시: SliderRangeAttribute

public class SliderRangeAttribute : PropertyAttribute
{
    public float min, max;
    public SliderRangeAttribute(float min, float max)
    {
        this.min = min;
        this.max = max;
    }
}

Drawer 구현

[CustomPropertyDrawer(typeof(SliderRangeAttribute))]
public class SliderRangeDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        SliderRangeAttribute range = (SliderRangeAttribute)attribute;
        if (property.propertyType == SerializedPropertyType.Float)
        {
            property.floatValue = EditorGUI.Slider(position, label, property.floatValue, range.min, range.max);
        }
        else
        {
            EditorGUI.LabelField(position, label.text, "Use SliderRange with float.");
        }
    }
}

7. 실무에서의 CustomEditor 활용 예시

7.1 게임 설정 에디터

ScriptableObject를 기반으로 각종 게임 설정값을 노출하고, 실시간 반영 가능하도록 제작.

7.2 스킬 트리 에디터

노드 기반 스킬 트리를 커스텀 에디터로 구현하여 드래그/드롭, 조건부 속성 표시 등을 처리.

7.3 애니메이션 트리 디버깅

Animator 상태 전이를 편하게 시각화하고 테스트 트리거를 버튼으로 제공.


8. 주의사항 및 팁

  • Editor 폴더에 반드시 위치해야 함.
  • EditorUtility.SetDirty() 또는 serializedObject.ApplyModifiedProperties()를 반드시 호출할 것.
  • 실시간 업데이트 필요 시 Repaint()를 호출.
  • target은 단일 오브젝트, targets는 복수 오브젝트.
  • 에디터 코드가 복잡할수록 EditorWindow 또는 EditorGUILayout 기반의 UI 툴로 분리 권장.

9. 마무리 및 학습 정리

Unity의 CustomEditor는 에디터 유저 경험을 향상시킬 수 있는 핵심 도구이다. Inspector를 원하는 대로 커스터마이징할 수 있으며, 팀 개발이나 게임 개발 툴을 만들 때 매우 유용하다. 특히 SerializedProperty 기반 접근은 안전성과 호환성을 보장하므로, 실무에서는 무조건 사용하는 것이 권장된다.

📌 핵심 요약

  • OnInspectorGUI를 통해 인스펙터 UI 재정의 가능
  • SerializedProperty를 사용하여 Undo, 멀티 편집 지원
  • PropertyDrawer는 필드 단위 UI 커스터마이징
  • ScriptableObject와 EditorWindow와 결합하면 툴 제작이 가능