본문 바로가기
TIL

커스텀 에디터(Custom Editor)

by vvin39 2025. 6. 4.

내일배움캠프 40일차 TIL

📌 커스텀 에디터(Custom Editor)

Unity에서 커스텀 에디터란, 개발자가 인스펙터창에 표시되는 컴포넌트의 편집 인터페이스를 직접 정의할 수 있도록 해주는 기능이다. 

 

기본적으로 Unity는 MonoBehaviour나 ScriptableObject 스크립트를 자동으로 인스펙터에 표시하지만, 마음에 들지 않을 때가 있다. 예를 들어, 여러 값을 한 번에 수정하거나, 특정 조건에 따라 다른 요소를 보여주고 싶을 때, 혹은 버튼을 눌러 직접 테스트 동작을 실행하고 싶을 때 등...

이럴 때 UnityEditor.Editor 클래스를 상속한 별도의 스크립트를 작성하면, 원하는 방식으로 인스펙터를 자유롭게 구성할 수 있다. 텍스트 입력 필드, 슬라이더, 버튼, 이미지, 메시지 박스 등 다양한 GUI 요소를 직접 배치하고 동작을 설정할 수 있어, 디자이너나 다른 개발자들이 더 쉽게 데이터를 수정하고 테스트할 수 있도록 도와준다.

단, 이 커스텀 에디터 스크립트는 반드시 Unity 프로젝트의 Editor 폴더 안에 있어야 하며, 실행 빌드에는 포함되지 않는다. 이렇게 분리함으로써, 게임 실행에는 영향을 주지 않으면서 에디터 상에서만 동작하도록 안전하게 설계되어 있다.

 

결과적으로,
커스텀 에디터는 Unity 개발 과정에서 생산성을 높이고, 작업 효율을 향상시키는 강력한 도구!

 

기본 사용 예시

1. 대상 클래스 (MyComponent.cs)

using UnityEngine;

public class MyComponent : MonoBehaviour
{
    public string myText = "Hello";
    public int myNumber = 10;
}

.

2. 커스텀 에디터 스크립트 (MyComponentEditor.cs)

Editor 폴더 안에 넣어야 작동합니다.

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyComponent))]  // 편집할 대상 지정
public class MyComponentEditor : Editor
{
    public override void OnInspectorGUI()
    {
        MyComponent myComponent = (MyComponent)target;

        // 기본 인스펙터 제외
        // DrawDefaultInspector();  <- 기본 인스펙터 그리려면 이걸 사용

        myComponent.myText = EditorGUILayout.TextField("텍스트", myComponent.myText);
        myComponent.myNumber = EditorGUILayout.IntSlider("숫자", myComponent.myNumber, 0, 100);

        if (GUILayout.Button("클릭 버튼"))
        {
            Debug.Log("버튼 클릭됨!");
        }

        // 변경사항 저장
        if (GUI.changed)
        {
            EditorUtility.SetDirty(myComponent);
        }
    }
}

 

 

주요 API 정리

EditorGUILayout.LabelField() 텍스트 레이블 표시
EditorGUILayout.IntField() / FloatField() / TextField() 다양한 타입 입력 필드
EditorGUILayout.ObjectField() Unity 오브젝트 참조 필드
EditorGUILayout.Slider() 슬라이더
GUILayout.Button() 버튼 생성
EditorGUI.BeginChangeCheck() / EndChangeCheck() 값 변경 감지
EditorUtility.SetDirty() 변경 사항 저장 표시

 

유용한 기능

1. 속성 그룹화 및 GUI 스타일

EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("속성 그룹");
EditorGUILayout.EndVertical();

 

2. 조건부 표시

if (myComponent.myNumber > 50)
{
    EditorGUILayout.HelpBox("숫자가 50보다 큽니다!", MessageType.Warning);
}

 

3. 비활성화 처리

EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField("읽기 전용", myComponent.myText);
EditorGUI.EndDisabledGroup();

 

고급 기능

기능  설명
CustomPropertyDrawer Serializable 필드의 렌더링 방식 커스터마이징
EditorWindow 완전한 에디터 창을 생성 (예: 툴 창)
ReorderableList 배열/리스트를 편하게 편집 가능
SerializedObject, SerializedProperty Undo/Redo, 멀티 선택 등을 제대로 처리하려면 필요

 

커스텀 에디터 언제 쓰면 좋을까?

  • 게임 디자이너/기획자가 편하게 조정하도록 GUI를 직관적으로 만들고 싶을 때
  • 특정 값이 바뀌면 자동 계산/검증을 수행해야 할 때
  • 복잡한 데이터 구조를 트리나 테이블 형태로 보여주고 싶을 때
  • 버튼 하나로 특정 기능(예: 데이터 초기화, 무기 생성 등)을 테스트하고 싶을 때

 

❗ 주의사항

Editor 클래스는 유니티 빌드에 포함되지 않도록 반드시 Editor 폴더 안에 위치해야 함!!

Editor 스크립트가 null을 참조하거나 예외가 생기면 인스펙터가 깨질 수 있으므로 예외 처리 필요

EditorUtility.SetDirty() 호출을 잊으면 변경사항이 저장되지 않음

 

Unity에서 ScriptableObject는 데이터 자산을 저장하고 재사용할 수 있도록 도와주는 데이터 중심 클래스. MonoBehaviour처럼 컴포넌트가 아닌 순수 데이터 저장용 오브젝트. 

 

 

📌 ScriptableObject란?

씬에 존재하지 않아도 되는 재사용 가능한 데이터 객체, 게임 오브젝트에 붙지 않고, 프로젝트 내부(Assets)에 독립적으로 존재할 수 있다.

 

https://vvin39.tistory.com/58

 

주요 특징 요약

항목  설명
상속 대상 ScriptableObject
저장 위치 에셋으로 프로젝트 폴더에 저장 (씬과 무관)
사용 목적 데이터 공유, 설정 저장, 상태 유지, 중앙 관리
메모리 씬에 직접 존재하지 않기 때문에 메모리 효율적
직렬화 인스펙터에 보이고 저장 가능
대표 예시 아이템 정보, 무기 스탯, 캐릭터 설정, 게임 설정, 이벤트 등

 

ScriptableObject 기본 사용법

1. 클래스 정의하기

using UnityEngine;

[CreateAssetMenu(fileName = "NewItemData", menuName = "Data/Item")]
public class ItemData : ScriptableObject
{
    public string itemName;
    public Sprite icon;
    public int value;
}

 

CreateAssetMenu를 붙이면 우클릭 > Create > Data > Item 메뉴가 생김

 

2. 에셋 생성하기

Unity 에디터에서

  1. Project 창 → 우클릭 → Create > Data > Item
  2. 생성된 에셋에 이름 지정
  3. 인스펙터에서 속성 입력.

 

3. ScriptableObject 사용하기

public class ItemHolder : MonoBehaviour
{
    public ItemData itemData;

    void Start()
    {
        Debug.Log("아이템 이름: " + itemData.itemName);
    }
}

이렇게 하면 여러 ItemHolder가 같은 ItemData 에셋을 참조하게 된다.

 

언제 쓰면 좋을까?

사용 상황  설명
데이터 관리 아이템, 스킬, 몬스터 스탯 등 반복 재사용되는 데이터
프리셋 저장 난이도 설정, 게임 규칙 등
중앙화된 상태 전체 게임 설정이나 글로벌 상태 저장
씬 간 공유 씬 전환 시에도 유지되는 데이터 (ex. 선택 캐릭터, 설정 등)

 

ScriptableObject vs MonoBehaviour

항목  ScriptableObject  MonoBehaviour
씬에 존재 여부 ❌ 필요 없음 ✅ 씬 내 존재
저장 위치 프로젝트(Assets) 폴더 씬 내부 또는 프리팹
주 용도 데이터 저장 및 공유 행동/로직 구현
재사용성 매우 높음 제한적
성능 더 가볍고 효율적 상대적으로 무거움
 

EX) 무기 데이터

[CreateAssetMenu(fileName = "NewWeapon", menuName = "Data/Weapon")]
public class WeaponData : ScriptableObject
{
    public string weaponName;
    public int damage;
    public float attackSpeed;
    public GameObject weaponPrefab;
}

그리고 WeaponController.cs에서 이렇게 사용 가능

public class WeaponController : MonoBehaviour
{
    public WeaponData weaponData;

    void Start()
    {
        Instantiate(weaponData.weaponPrefab);
        Debug.Log($"무기: {weaponData.weaponName}, 데미지: {weaponData.damage}");
    }
}

 

고급 기능 : 런타임 데이터 관리

ScriptableObject는 런타임에 수정하면 에셋 자체가 바뀌지 않는다. 그러나 공유된 참조이기 때문에, 런타임에서 값이 바뀌면 모든 참조에서 반영된다.

예를 들어, 퀘스트 상태를 ScriptableObject로 들고 있으면

[CreateAssetMenu(menuName = "Data/Quest")]
public class QuestData : ScriptableObject
{
    public bool isCompleted;
}

주의: 게임 플레이 중에 isCompleted = true로 바꾸면 다른 오브젝트들도 그 변경을 감지! 초기화하려면 씬 로딩 시 복사본(clone)을 만들어 써야 함

 

개인과제할 때 ScriptableObject와 인벤토리 시스템을 연결해서 사용해봐야겠다!

 

 

 

 

최근댓글

최근글

skin by © 2024 ttuttak