#if UNITY_EDITOR using UnityEngine; using UnityEditor; using Colosseum.Enemy; namespace Colosseum.Editor { /// /// BossEnemy 커스텀 인스펙터. /// 페이즈 정보, HP, 상태를 시각적으로 표시합니다. /// [CustomEditor(typeof(BossEnemy))] public class BossEnemyEditor : UnityEditor.Editor { private BossEnemy boss; private bool showPhaseDetails = true; private bool showThreatInfo = true; private bool showDebugTools = true; private int selectedPhaseIndex = 0; private void OnEnable() { boss = (BossEnemy)target; } public override void OnInspectorGUI() { // 기본 인스펙터 그리기 DrawDefaultInspector(); if (!Application.isPlaying) { EditorGUILayout.HelpBox("런타임 디버그 정보는 플레이 모드에서만 표시됩니다.", MessageType.Info); return; } EditorGUILayout.Space(10); // 상태 요약 DrawStatusSummary(); EditorGUILayout.Space(10); // 페이즈 정보 DrawPhaseInfo(); EditorGUILayout.Space(10); // 위협 정보 DrawThreatInfo(); EditorGUILayout.Space(10); // 디버그 도구 DrawDebugTools(); } /// /// 상태 요약 표시 /// private void DrawStatusSummary() { EditorGUILayout.LabelField("상태 요약", EditorStyles.boldLabel); EditorGUI.indentLevel++; // HP 바 float hpPercent = boss.MaxHealth > 0 ? boss.CurrentHealth / boss.MaxHealth : 0f; DrawProgressBar("HP", hpPercent, GetHealthColor(hpPercent), $"{boss.CurrentHealth:F0} / {boss.MaxHealth:F0}"); // 상태 정보 EditorGUILayout.LabelField("현재 페이즈", $"{boss.CurrentPhaseIndex + 1} / {boss.TotalPhases}"); EditorGUILayout.LabelField("상태", GetStatusText()); if (boss.CurrentPhase != null) { EditorGUILayout.LabelField("페이즈명", boss.CurrentPhase.PhaseName); } EditorGUI.indentLevel--; } /// /// 페이즈 상세 정보 표시 /// private void DrawPhaseInfo() { showPhaseDetails = EditorGUILayout.Foldout(showPhaseDetails, "페이즈 상세 정보", true); if (!showPhaseDetails) return; EditorGUI.indentLevel++; var phasesProp = serializedObject.FindProperty("phases"); if (phasesProp == null || phasesProp.arraySize == 0) { EditorGUILayout.HelpBox("등록된 페이즈가 없습니다.", MessageType.Warning); EditorGUI.indentLevel--; return; } for (int i = 0; i < phasesProp.arraySize; i++) { var phaseProp = phasesProp.GetArrayElementAtIndex(i); var phase = phaseProp.objectReferenceValue as BossPhaseData; if (phase == null) continue; bool isCurrentPhase = i == boss.CurrentPhaseIndex; bool isCompleted = i < boss.CurrentPhaseIndex; // 페이즈 헤더 GUIStyle phaseStyle = new GUIStyle(EditorStyles.foldout); if (isCurrentPhase) phaseStyle.fontStyle = FontStyle.Bold; EditorGUILayout.BeginHorizontal(); // 상태 아이콘 string statusIcon = isCurrentPhase ? "▶" : (isCompleted ? "✓" : "○"); GUIContent phaseLabel = new GUIContent($"{statusIcon} Phase {i + 1}: {phase.PhaseName}"); EditorGUILayout.LabelField(phaseLabel, GUILayout.Width(200)); // 전환 조건 EditorGUILayout.LabelField($"[{phase.TransitionType}]", EditorStyles.miniLabel, GUILayout.Width(100)); EditorGUILayout.EndHorizontal(); if (isCurrentPhase) { EditorGUI.indentLevel++; EditorGUILayout.LabelField($"전환 조건: {GetTransitionConditionText(phase)}"); EditorGUILayout.LabelField($"경과 시간: {boss.PhaseElapsedTime:F1}초"); EditorGUI.indentLevel--; } EditorGUILayout.Space(2); } EditorGUI.indentLevel--; } /// /// 위협 정보 표시 /// private void DrawThreatInfo() { showThreatInfo = EditorGUILayout.Foldout(showThreatInfo, "위협 정보", true); if (!showThreatInfo) return; EditorGUI.indentLevel++; if (!boss.UseThreatSystem) { EditorGUILayout.HelpBox("위협 시스템이 비활성화되어 있습니다.", MessageType.Info); EditorGUI.indentLevel--; return; } EditorGUILayout.LabelField("위협 테이블", EditorStyles.boldLabel); EditorGUILayout.TextArea(boss.GetThreatDebugSummary(), GUILayout.MinHeight(70f)); if (GUILayout.Button("위협 초기화")) { boss.ClearAllThreat(); } EditorGUI.indentLevel--; } /// /// 디버그 도구 표시 /// private void DrawDebugTools() { showDebugTools = EditorGUILayout.Foldout(showDebugTools, "디버그 도구", true); if (!showDebugTools) return; EditorGUI.indentLevel++; EditorGUILayout.HelpBox("이 도구는 서버에서만 작동합니다.", MessageType.Info); // 페이즈 강제 전환 EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("페이즈 강제 전환", GUILayout.Width(120)); selectedPhaseIndex = EditorGUILayout.IntSlider(selectedPhaseIndex, 0, Mathf.Max(0, boss.TotalPhases - 1)); if (GUILayout.Button("전환", GUILayout.Width(60))) { if (Application.isPlaying) { boss.ForcePhaseTransition(selectedPhaseIndex); } } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(5); // 현재 페이즈 재시작 if (GUILayout.Button("현재 페이즈 재시작")) { if (Application.isPlaying) { boss.RestartCurrentPhase(); } } EditorGUILayout.Space(5); // HP 조작 EditorGUILayout.LabelField("HP 조작", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("HP 10%")) { SetBossHP(0.1f); } if (GUILayout.Button("HP 30%")) { SetBossHP(0.3f); } if (GUILayout.Button("HP 50%")) { SetBossHP(0.5f); } if (GUILayout.Button("HP 100%")) { SetBossHP(1f); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(5); // 커스텀 조건 EditorGUILayout.LabelField("커스텀 조건 설정", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("조건 ID:", GUILayout.Width(60)); string conditionId = EditorGUILayout.TextField("Enraged"); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("활성화")) { boss.SetCustomCondition(conditionId, true); } if (GUILayout.Button("비활성화")) { boss.SetCustomCondition(conditionId, false); } EditorGUILayout.EndHorizontal(); EditorGUI.indentLevel--; } /// /// HP 설정 (서버에서만) /// private void SetBossHP(float percent) { if (!Application.isPlaying) return; float targetHP = boss.MaxHealth * percent; float damage = boss.CurrentHealth - targetHP; if (damage > 0) { boss.TakeDamage(damage); } else if (damage < 0) { boss.Heal(-damage); } } /// /// 진행 바 그리기 /// private void DrawProgressBar(string label, float value, Color color, string text = "") { Rect rect = EditorGUILayout.GetControlRect(); rect.height = 20f; // 레이블 Rect labelRect = new Rect(rect.x, rect.y, 60, rect.height); EditorGUI.LabelField(labelRect, label); // 바 Rect barRect = new Rect(rect.x + 65, rect.y, rect.width - 65, rect.height); EditorGUI.DrawRect(barRect, new Color(0.2f, 0.2f, 0.2f)); Rect fillRect = new Rect(barRect.x, barRect.y, barRect.width * Mathf.Clamp01(value), barRect.height); EditorGUI.DrawRect(fillRect, color); // 텍스트 if (!string.IsNullOrEmpty(text)) { GUIStyle centeredStyle = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleCenter }; EditorGUI.LabelField(barRect, text, centeredStyle); } } /// /// HP 비율에 따른 색상 반환 /// private Color GetHealthColor(float percent) { if (percent > 0.6f) return new Color(0.2f, 0.8f, 0.2f); // 녹색 if (percent > 0.3f) return new Color(0.9f, 0.7f, 0.1f); // 노란색 return new Color(0.9f, 0.2f, 0.2f); // 빨간색 } /// /// 상태 텍스트 반환 /// private string GetStatusText() { if (boss.IsDead) return "사망"; if (boss.IsTransitioning) return "페이즈 전환 중"; return "활성"; } /// /// 전환 조건 텍스트 반환 /// private string GetTransitionConditionText(BossPhaseData phase) { return phase.TransitionType switch { PhaseTransitionType.HealthPercent => $"HP ≤ {phase.HealthPercentThreshold * 100:F0}%", PhaseTransitionType.TimeElapsed => $"시간 ≥ {phase.TimeThreshold:F0}초", PhaseTransitionType.CustomCondition => $"조건: {phase.CustomConditionId}", PhaseTransitionType.Manual => "수동 전환", _ => "알 수 없음" }; } } } #endif