using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을 using UnityEditor; // 유니티 에디터 기능을 사용할거에요 -> UnityEditor를 namespace GameSystems.Optimization.Editor // 에디터 네임스페이스를 정의할거에요 -> GameSystems.Optimization.Editor로 { [CustomEditor(typeof(PlayerRangeManager))] // 커스텀 에디터 대상을 지정할거에요 -> PlayerRangeManager 클래스로 public sealed class PlayerRangeManagerEditor : UnityEditor.Editor // 클래스를 선언할거에요 -> 에디터 상속을 받는 PlayerRangeManagerEditor를 { private SerializedProperty _configProperty; // 변수를 선언할거에요 -> 설정 속성을 연결할 _configProperty를 private SerializedProperty _parentsProperty; // 변수를 선언할거에요 -> 부모 목록 속성을 연결할 _parentsProperty를 private void OnEnable() // 에디터가 활성화될 때 실행할거에요 -> OnEnable 함수를 { _configProperty = serializedObject.FindProperty("_config"); // 찾을거에요 -> 대상의 "_config" 변수를 // ⭐ [에러 해결] 리스트 이름 '_environmentParents'를 정확히 찾아옵니다. _parentsProperty = serializedObject.FindProperty("_environmentParents"); // 찾을거에요 -> 대상의 "_environmentParents" 변수를 } public override void OnInspectorGUI() // 인스펙터 화면을 그릴거에요 -> OnInspectorGUI 함수를 { PlayerRangeManager manager = (PlayerRangeManager)target; // 변수를 설정할거에요 -> 현재 선택된 매니저 객체로 serializedObject.Update(); // 실행할거에요 -> 객체의 최신 데이터를 불러오기를 // === 헤더 === DrawMainHeader(); // 함수를 실행할거에요 -> 메인 헤더를 그리는 EditorGUILayout.Space(5); // 공간을 줄거에요 -> 5픽셀만큼의 여백을 // === 설정 영역 === EditorGUILayout.BeginVertical(EditorStyles.helpBox); // 수직 영역을 시작할거에요 -> 헬프박스 스타일로 GUILayout.Label("필수 설정", EditorStyles.boldLabel); // 라벨을 표시할거에요 -> "필수 설정"이라는 제목을 EditorGUILayout.PropertyField(_configProperty); // 그려낼거에요 -> 설정 에셋 입력 필드를 // ⭐ [에러 해결] 리스트를 안전하게 그립니다. if (_parentsProperty != null) // 조건이 맞으면 실행할거에요 -> 부모 속성이 유효하다면 { EditorGUILayout.PropertyField(_parentsProperty, new GUIContent("Environment Parents"), true); // 그려낼거에요 -> 자식 요소까지 포함한 리스트 필드를 } EditorGUILayout.EndVertical(); // 수직 영역을 종료할거에요 // === 실시간 통계 === if (Application.isPlaying && manager.IsInitialized) // 조건이 맞으면 실행할거에요 -> 게임 중이고 초기화되었다면 { EditorGUILayout.Space(5); // 여백을 줄거에요 EditorGUILayout.BeginVertical(EditorStyles.helpBox); // 수직 영역을 시작할거에요 GUILayout.Label("실시간 통계", EditorStyles.boldLabel); // 제목을 표시할거에요 -> "실시간 통계"를 EditorGUILayout.LabelField("총 오브젝트:", manager.TotalObjectCount.ToString()); // 텍스트를 표시할거에요 -> 총 개수 정보를 EditorGUILayout.LabelField("표시 중:", manager.VisibleObjectCount.ToString()); // 텍스트를 표시할거에요 -> 현재 표시 개수 정보를 EditorGUILayout.LabelField("컬링 효율:", $"{(manager.CullingEfficiency * 100f):F1}%"); // 텍스트를 표시할거에요 -> 효율 퍼센트 정보를 EditorGUILayout.EndVertical(); // 수직 영역을 종료할거에요 } EditorGUILayout.Space(5); // 여백을 줄거에요 // === 컨트롤 버튼 === if (GUILayout.Button("🔄 오브젝트 목록 새로고침", GUILayout.Height(30))) // 조건이 맞으면 실행할거에요 -> 버튼을 눌렀다면 { if (Application.isPlaying) manager.RefreshObjectList(); // 실행할거에요 -> 실행 중일 때 목록 갱신 기능을 } serializedObject.ApplyModifiedProperties(); // 실행할거에요 -> 변경된 데이터를 실제 객체에 반영하기를 if (Application.isPlaying) Repaint(); // 실행할거에요 -> 실행 중일 때 화면을 다시 그리기를 } private void DrawMainHeader() // 함수를 선언할거에요 -> 메인 헤더를 그리는 DrawMainHeader를 { EditorGUILayout.BeginVertical(EditorStyles.helpBox); // 수직 영역을 시작할거에요 GUILayout.Label("Player Range Manager", new GUIStyle(EditorStyles.boldLabel) { fontSize = 14, alignment = TextAnchor.MiddleCenter }); // 큰 제목을 그릴거에요 -> 중앙 정렬된 스타일로 EditorGUILayout.EndVertical(); // 수직 영역을 종료할거에요 } } }