서론
- 프로젝트 진행 중 파티클 시스템을 사용하는데 SortLayerID를 하나씩 세팅을 해줘야하는 번거로움이 있었다.
해당 번거로움을 개선하기 위해서 Awake에 Effect라는 SortLayerID를 부여하도록 했지만 이번에 기획자분도 새롭게 참여를 할 것 같아 가시성이 좋게 진행하는 방법이 뭐가 있을까 싶었다.
그렇게 찾은 기능이 Custom Editor기능이다.
Custom Editor
커스텀 에디터의 목적
1. 인스펙터 UI 제어
* 유니티 기본 인스펙터는 [SerializeField]된 필드를 자동으로 나열해 보여준다.
* 하지만 일부 데이터는 슬라이더, 드롭다운,색상 선택기 등으로 보여주는게 훨씬 직관적이다.
EX) SortinLayerID는 단순 int입력으로 값을 지정했다면 커스텀 에디터를 통해 SortingLayer 목록 드랍다운으로
보여준다.
2. 작업 효율성 향상
* 에디터에 버튼이나 툴 패널을 추가해서 반복적인 작업을 자동화할 수 있다.
EX) NavMesh 다시 굽기 버튼, Prefab 초기화 등등
3. 데이터 검증 및 제약
* 잘못된 값을 입력하지 못하게 막거나, 입력 시 자동으로 보정할 수 있다.
4. 시각화 / 디버깅
* SceneView와 Inspector를 연결해서 데이터 시각화를 할 수 있다.
EX) AI의 시야 범위를 SceneView에 원으로 그려주기
결론
- 커스텀 데이터의 목적은 "개발 편의성과 생산성 향상, 잘못된 데이터 입력 방지, 디버깅과 시각화 보조가 핵심"이다.
연습
*커스텀 에디터를 제작하기전에 내가 사용할 클래스를 미리 제작해야한다.
using UnityEngine;
/// <summary>
/// 부모 오브젝트 포함 모든 자식의 ParticleSystemRenderer의
/// Sorting Layer를 지정된 레이어로 자동 변경하는 스크립트.
/// </summary>
public class ParticleSortingLayerSetter : MonoBehaviour
{
[Tooltip("적용할 Sorting Layer")]
[SerializeField] private string targetSortingLayer = "Default";
private void Awake()
{
ApplySortingLayer();
}
private void ApplySortingLayer()
{
int layerId = SortingLayer.NameToID(targetSortingLayer);
if (layerId == 0 && targetSortingLayer != "Default")
{
Debug.LogWarning($"[ParticleSortingLayerSetter] 지정한 SortingLayer '{targetSortingLayer}'가 존재하지 않습니다.");
return;
}
ParticleSystemRenderer[] renderers = GetComponentsInChildren<ParticleSystemRenderer>(true);
foreach (var psr in renderers)
{
psr.sortingLayerID = layerId;
}
Debug.Log($"[ParticleSortingLayerSetter] {renderers.Length}개의 파티클 SortingLayer를 '{targetSortingLayer}'로 변경 완료!");
}
}

해당 스크립트를 객체에 넣어주면 이미지처럼 나온다.
사용하는데 지장은 없겠지만 여러 오브젝트를 세팅하기에는 번거로움이 있다.
커스텀 에디터
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(ParticleSortingLayerSetter))]
public class ParticleSortingLayerSetterEditor : Editor
{
public override void OnInspectorGUI()
{
ParticleSortingLayerSetter script = (ParticleSortingLayerSetter)target;
// 현재 Sorting Layer 목록 가져오기 (안전한 방식)
string[] sortingLayers = SortingLayer.layers != null ? new string[SortingLayer.layers.Length] : new string[0];
for (int i = 0; i < sortingLayers.Length; i++)
{
sortingLayers[i] = SortingLayer.layers[i].name;
}
// 현재 값 가져오기
string currentLayer = GetPrivateField<string>(script, "targetSortingLayer");
int currentIndex = Mathf.Max(0, System.Array.IndexOf(sortingLayers, currentLayer));
// 드롭다운
int newIndex = EditorGUILayout.Popup("Target Sorting Layer", currentIndex, sortingLayers);
if (sortingLayers.Length > 0 && newIndex >= 0 && newIndex < sortingLayers.Length)
{
string newLayer = sortingLayers[newIndex];
if (newLayer != currentLayer)
{
Undo.RecordObject(script, "Change Sorting Layer");
SetPrivateField(script, "targetSortingLayer", newLayer);
EditorUtility.SetDirty(script);
}
}
// 나머지 인스펙터 기본 그리기
DrawDefaultInspector();
}
// private 필드 접근용 유틸
private T GetPrivateField<T>(object obj, string fieldName)
{
var field = obj.GetType().GetField(fieldName,
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
return (T)field.GetValue(obj);
}
private void SetPrivateField<T>(object obj, string fieldName, T value)
{
var field = obj.GetType().GetField(fieldName,
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
field.SetValue(obj, value);
}
}
여기서 주목해야할 클래스가 있는데
EditorGUILayout이다. UI 그리기 함수 집합의 기능을 가지고 있으며 커스텀 에디터를 제작할때 반 필수적인
클래스이다.
https://docs.unity3d.com/ScriptReference/EditorGUILayout.html?utm_source=chatgpt.com

이미지처럼 드랍다운이 생성되었다!

또는 해당 이미지처럼 직렬화 없이 인스펙터에서 데이터 확인이 가능하게 제작할수있다.
'유니티' 카테고리의 다른 글
| 유니티 자동화 툴 만들기<프로젝트 에셋 관련 기능 정리> (0) | 2025.11.01 |
|---|---|
| 오디오 믹서 (Aduio Mixer) (1) | 2025.09.14 |
| 스크립팅 백엔드 (2) | 2025.03.30 |
| RectTransformUtility (3) | 2025.03.19 |
| RectTransformUntility에 대해서 (17) | 2025.01.07 |