Unity Editor 스크립팅 — 커스텀 툴 제작 가이드
Unity Editor 스크립팅은 UnityEditor 네임스페이스를 활용해 에디터 자체를 확장하는 기술입니다. 커스텀 Inspector, 툴 창, 에셋 파이프라인 훅을 만들어 팀 워크플로를 자동화할 수 있습니다.
Editor 스크립트는 반드시
Editor/폴더 안에 위치해야 빌드에 포함되지 않습니다.
1. 커스텀 Inspector
섹션 제목: “1. 커스텀 Inspector”using UnityEngine;using UnityEditor;
[CustomEditor(typeof(EnemyConfig))]public class EnemyConfigEditor : Editor{ public override void OnInspectorGUI() { DrawDefaultInspector();
var config = (EnemyConfig)target;
EditorGUILayout.Space(); EditorGUILayout.LabelField("미리보기", EditorStyles.boldLabel);
GUI.color = Color.red; if (GUILayout.Button("스탯 초기화")) { config.ResetStats(); EditorUtility.SetDirty(config); } GUI.color = Color.white; }}2. SerializedProperty 기반 Inspector
섹션 제목: “2. SerializedProperty 기반 Inspector”[CustomEditor(typeof(WeaponData))]public class WeaponDataEditor : Editor{ SerializedProperty _damage; SerializedProperty _range; SerializedProperty _type;
void OnEnable() { _damage = serializedObject.FindProperty("damage"); _range = serializedObject.FindProperty("range"); _type = serializedObject.FindProperty("weaponType"); }
public override void OnInspectorGUI() { serializedObject.Update();
EditorGUILayout.PropertyField(_type);
var type = (WeaponType)_type.enumValueIndex; if (type == WeaponType.Ranged) EditorGUILayout.PropertyField(_range);
EditorGUILayout.PropertyField(_damage);
serializedObject.ApplyModifiedProperties(); }}3. EditorWindow — 커스텀 툴 창
섹션 제목: “3. EditorWindow — 커스텀 툴 창”using UnityEditor;using UnityEngine;
public class LevelBuilderWindow : EditorWindow{ [MenuItem("Tools/Level Builder")] public static void Open() => GetWindow<LevelBuilderWindow>("Level Builder");
private int _gridSize = 10; private GameObject _prefab;
void OnGUI() { GUILayout.Label("레벨 빌더 설정", EditorStyles.boldLabel);
_gridSize = EditorGUILayout.IntSlider("그리드 크기", _gridSize, 1, 50); _prefab = (GameObject)EditorGUILayout.ObjectField( "타일 프리팹", _prefab, typeof(GameObject), false);
EditorGUI.BeginDisabledGroup(_prefab == null); if (GUILayout.Button("그리드 생성")) GenerateGrid(); EditorGUI.EndDisabledGroup(); }
void GenerateGrid() { for (int x = 0; x < _gridSize; x++) for (int z = 0; z < _gridSize; z++) { var obj = (GameObject)PrefabUtility.InstantiatePrefab(_prefab); obj.transform.position = new Vector3(x, 0, z); Undo.RegisterCreatedObjectUndo(obj, "Generate Grid"); } }}4. MenuItem 속성
섹션 제목: “4. MenuItem 속성”public static class MenuItems{ // 메뉴 추가 [MenuItem("Tools/씬 정리")] static void CleanupScene() { /* ... */ }
// 우클릭 컨텍스트 메뉴 [MenuItem("GameObject/Custom/빈 부모 생성", false, 0)] static void CreateEmptyParent() { var selected = Selection.activeGameObject; var parent = new GameObject("Group"); parent.transform.SetParent(selected?.transform.parent); parent.transform.position = selected?.transform.position ?? Vector3.zero; selected?.transform.SetParent(parent.transform); Undo.RegisterCreatedObjectUndo(parent, "Create Parent"); }
// 유효성 검사 (회색 처리) [MenuItem("Tools/씬 정리", true)] static bool CleanupSceneValidate() => !EditorApplication.isPlaying;}5. Gizmos로 씬 뷰 시각화
섹션 제목: “5. Gizmos로 씬 뷰 시각화”[ExecuteAlways]public class PatrolRoute : MonoBehaviour{ public Transform[] waypoints;
void OnDrawGizmos() { if (waypoints == null || waypoints.Length < 2) return;
Gizmos.color = Color.cyan; for (int i = 0; i < waypoints.Length; i++) { Gizmos.DrawSphere(waypoints[i].position, 0.3f); Gizmos.DrawLine( waypoints[i].position, waypoints[(i + 1) % waypoints.Length].position); } }}6. AssetPostprocessor — 에셋 파이프라인 훅
섹션 제목: “6. AssetPostprocessor — 에셋 파이프라인 훅”public class TexturePostprocessor : AssetPostprocessor{ void OnPreprocessTexture() { var importer = (TextureImporter)assetImporter;
// UI 폴더의 텍스처는 자동으로 Sprite 타입으로 설정 if (assetPath.Contains("/UI/")) { importer.textureType = TextureImporterType.Sprite; importer.maxTextureSize = 512; } }}Editor 스크립팅은 팀의 반복 작업을 자동화하는 핵심 도구입니다. 커스텀 Inspector로 데이터 입력 오류를 줄이고, EditorWindow로 레벨 디자인 툴을 만들며, AssetPostprocessor로 에셋 규칙을 강제하면 프로젝트 품질과 생산성이 동시에 향상됩니다.