feat: 런타임 보스 디버그 패널 및 MCP 커맨드 추가
- HUD 우측 하단 접이식 디버그 패널 (DebugPanelUI): 보스 HP 슬라이더/직접입력/프리셋, 페이즈 전환, 리스폰, 보호막, 이상상태 적용 - MCP execute_menu_item으로 호출 가능한 MenuItem 커맨드 (DebugBossMenuItems): HP/페이즈/보호막 제어, 상태 조회, 임의 값 설정 지원 - BossEnemyEditor 인스펙터 HP 조작 확장: 퍼센트 슬라이더 및 직접 HP 입력 추가 - Test 씬 UI Canvas 하위 DebugPanel GameObject 배치 - UNITY_EDITOR || DEVELOPMENT_BUILD 가드로 릴리즈 빌드 미포함
This commit is contained in:
@@ -17,6 +17,8 @@ namespace Colosseum.Editor
|
||||
private bool showThreatInfo = true;
|
||||
private bool showDebugTools = true;
|
||||
private int selectedPhaseIndex = 0;
|
||||
private float debugHPPercent = 1f;
|
||||
private float debugHPValue = 0f;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
@@ -214,6 +216,37 @@ namespace Colosseum.Editor
|
||||
|
||||
// HP 조작
|
||||
EditorGUILayout.LabelField("HP 조작", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
// 현재 HP 표시
|
||||
EditorGUILayout.LabelField("현재",
|
||||
$"{boss.CurrentHealth:F0} / {boss.MaxHealth:F0} ({(boss.MaxHealth > 0 ? boss.CurrentHealth / boss.MaxHealth * 100f : 0f):F1}%)");
|
||||
|
||||
// 퍼센트 슬라이더
|
||||
EditorGUI.BeginChangeCheck();
|
||||
debugHPPercent = EditorGUILayout.Slider("퍼센트", debugHPPercent, 0f, 1f);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
SetBossHP(debugHPPercent);
|
||||
}
|
||||
|
||||
// 직접 HP 값 입력
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
debugHPValue = EditorGUILayout.FloatField("직접 입력", debugHPValue);
|
||||
EditorGUILayout.LabelField($"/ {boss.MaxHealth:F0}", GUILayout.Width(80));
|
||||
if (GUILayout.Button("적용", GUILayout.Width(60)))
|
||||
{
|
||||
float clamped = Mathf.Clamp(debugHPValue, 0f, boss.MaxHealth);
|
||||
float percent = boss.MaxHealth > 0 ? clamped / boss.MaxHealth : 0f;
|
||||
debugHPPercent = percent;
|
||||
SetBossHP(percent);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
// 빠른 HP 설정 버튼
|
||||
EditorGUILayout.Space(3);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("HP 10%"))
|
||||
{
|
||||
|
||||
255
Assets/_Game/Scripts/Editor/DebugBossMenuItems.cs
Normal file
255
Assets/_Game/Scripts/Editor/DebugBossMenuItems.cs
Normal file
@@ -0,0 +1,255 @@
|
||||
using System.Text;
|
||||
|
||||
using Colosseum.Enemy;
|
||||
using Colosseum.Abnormalities;
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Colosseum.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// MCP (execute_menu_item) 또는 에디터 메뉴에서 호출 가능한 보스 디버그 커맨드.
|
||||
/// 파라미터가 필요한 연산은 static 필드를 설정한 뒤 Custom MenuItem을 호출합니다.
|
||||
/// </summary>
|
||||
public static class DebugBossMenuItems
|
||||
{
|
||||
private const string StunAbnormalityPath = "Assets/_Game/Data/Abnormalities/Data_Abnormality_Player_Stun.asset";
|
||||
private const string SilenceAbnormalityPath = "Assets/_Game/Data/Abnormalities/Data_Abnormality_Player_Silence.asset";
|
||||
|
||||
// ── MCP가 설정 후 Custom 메뉴 호출하는 파라미터 필드 ──
|
||||
|
||||
/// <summary>
|
||||
/// Set Boss HP Custom에서 사용할 퍼센트 (0.0 ~ 1.0).
|
||||
/// MCP는 script_apply_edits로 이 값을 변경한 뒤 Custom 메뉴를 호출합니다.
|
||||
/// </summary>
|
||||
public static float customHPPercent = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Force Phase Custom에서 사용할 페이즈 인덱스 (0-based).
|
||||
/// </summary>
|
||||
public static int customPhaseIndex = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Apply Shield Custom에서 사용할 보호막 수치.
|
||||
/// </summary>
|
||||
public static float customShieldAmount = 1000f;
|
||||
|
||||
// ── 보스 HP ──
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 10%")]
|
||||
private static void SetHP10() => SetBossHP(0.1f);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 25%")]
|
||||
private static void SetHP25() => SetBossHP(0.25f);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 50%")]
|
||||
private static void SetHP50() => SetBossHP(0.5f);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 75%")]
|
||||
private static void SetHP75() => SetBossHP(0.75f);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP 100%")]
|
||||
private static void SetHP100() => SetBossHP(1f);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Set HP Custom")]
|
||||
private static void SetHPCustom() => SetBossHP(Mathf.Clamp01(customHPPercent));
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Full Heal")]
|
||||
private static void FullHeal()
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
boss.Heal(boss.MaxHealth);
|
||||
Debug.Log($"[Debug] 보스 HP 풀회복 | HP={boss.CurrentHealth:F0}/{boss.MaxHealth:F0}");
|
||||
}
|
||||
|
||||
// ── 보스 제어 ──
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Force Phase 0")]
|
||||
private static void ForcePhase0() => ForcePhase(0);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Force Phase 1")]
|
||||
private static void ForcePhase1() => ForcePhase(1);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Force Phase 2")]
|
||||
private static void ForcePhase2() => ForcePhase(2);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Force Phase Custom")]
|
||||
private static void ForcePhaseCustom() => ForcePhase(customPhaseIndex);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Restart Current Phase")]
|
||||
private static void RestartPhase()
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
boss.RestartCurrentPhase();
|
||||
Debug.Log($"[Debug] 보스 현재 페이즈 재시작 | Phase={boss.CurrentPhaseIndex}");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Respawn")]
|
||||
private static void Respawn()
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
boss.Respawn();
|
||||
Debug.Log($"[Debug] 보스 리스폰 | HP={boss.CurrentHealth:F0}/{boss.MaxHealth:F0}");
|
||||
}
|
||||
|
||||
// ── 보호막 ──
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Shield 500")]
|
||||
private static void ApplyShield500() => ApplyShield(500f);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Shield 1000")]
|
||||
private static void ApplyShield1000() => ApplyShield(1000f);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Shield 5000")]
|
||||
private static void ApplyShield5000() => ApplyShield(5000f);
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Shield Custom")]
|
||||
private static void ApplyShieldCustom() => ApplyShield(Mathf.Max(0f, customShieldAmount));
|
||||
|
||||
// ── 이상상태 ──
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Stun")]
|
||||
private static void ApplyStun()
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
|
||||
AbnormalityData data = AssetDatabase.LoadAssetAtPath<AbnormalityData>(StunAbnormalityPath);
|
||||
if (data == null)
|
||||
{
|
||||
Debug.LogWarning($"[Debug] 기절 에셋을 찾지 못했습니다: {StunAbnormalityPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
AbnormalityManager am = boss.GetComponent<AbnormalityManager>();
|
||||
if (am != null)
|
||||
{
|
||||
am.ApplyAbnormality(data, boss.gameObject);
|
||||
Debug.Log($"[Debug] 보스 기절 적용 | {data.abnormalityName}");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Apply Silence")]
|
||||
private static void ApplySilence()
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
|
||||
AbnormalityData data = AssetDatabase.LoadAssetAtPath<AbnormalityData>(SilenceAbnormalityPath);
|
||||
if (data == null)
|
||||
{
|
||||
Debug.LogWarning($"[Debug] 침묵 에셋을 찾지 못했습니다: {SilenceAbnormalityPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
AbnormalityManager am = boss.GetComponent<AbnormalityManager>();
|
||||
if (am != null)
|
||||
{
|
||||
am.ApplyAbnormality(data, boss.gameObject);
|
||||
Debug.Log($"[Debug] 보스 침묵 적용 | {data.abnormalityName}");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Clear Threat")]
|
||||
private static void ClearThreat()
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
boss.ClearAllThreat();
|
||||
Debug.Log("[Debug] 보스 위협 초기화");
|
||||
}
|
||||
|
||||
// ── 상태 조회 ──
|
||||
|
||||
[MenuItem("Tools/Colosseum/Debug/Boss/Log Status")]
|
||||
private static void LogStatus()
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine($"[Debug] 보스 상태 | {boss.name}");
|
||||
sb.AppendLine($" HP: {boss.CurrentHealth:F0} / {boss.MaxHealth:F0} ({(boss.MaxHealth > 0 ? boss.CurrentHealth / boss.MaxHealth * 100f : 0f):F1}%)");
|
||||
sb.AppendLine($" Shield: {boss.Shield:F0}");
|
||||
sb.AppendLine($" Phase: {boss.CurrentPhaseIndex + 1} / {boss.TotalPhases}");
|
||||
sb.AppendLine($" IsDead: {boss.IsDead}");
|
||||
|
||||
if (boss.CurrentPhase != null)
|
||||
sb.AppendLine($" PhaseName: {boss.CurrentPhase.PhaseName}");
|
||||
|
||||
Debug.Log(sb.ToString());
|
||||
}
|
||||
|
||||
// ── 내부 구현 ──
|
||||
|
||||
/// <summary>
|
||||
/// 활성 보스 찾기
|
||||
/// </summary>
|
||||
private static BossEnemy FindBoss()
|
||||
{
|
||||
if (!EditorApplication.isPlaying)
|
||||
{
|
||||
Debug.LogWarning("[Debug] 플레이 모드에서만 사용할 수 있습니다.");
|
||||
return null;
|
||||
}
|
||||
|
||||
BossEnemy boss = BossEnemy.ActiveBoss != null
|
||||
? BossEnemy.ActiveBoss
|
||||
: Object.FindFirstObjectByType<BossEnemy>();
|
||||
|
||||
if (boss == null)
|
||||
Debug.LogWarning("[Debug] 활성 보스를 찾지 못했습니다.");
|
||||
|
||||
return boss;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보스 HP를 퍼센트로 설정
|
||||
/// </summary>
|
||||
private static void SetBossHP(float percent)
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
|
||||
float targetHP = boss.MaxHealth * percent;
|
||||
float diff = boss.CurrentHealth - targetHP;
|
||||
|
||||
if (diff > 0f)
|
||||
boss.TakeDamage(diff);
|
||||
else if (diff < 0f)
|
||||
boss.Heal(-diff);
|
||||
|
||||
Debug.Log($"[Debug] 보스 HP 설정 {percent * 100f:F0}% | HP={boss.CurrentHealth:F0}/{boss.MaxHealth:F0}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 페이즈 강제 전환
|
||||
/// </summary>
|
||||
private static void ForcePhase(int index)
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
|
||||
index = Mathf.Clamp(index, 0, Mathf.Max(0, boss.TotalPhases - 1));
|
||||
boss.ForcePhaseTransition(index);
|
||||
Debug.Log($"[Debug] 보스 페이즈 강제 전환 | Phase={index}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보호막 적용
|
||||
/// </summary>
|
||||
private static void ApplyShield(float amount)
|
||||
{
|
||||
BossEnemy boss = FindBoss();
|
||||
if (boss == null) return;
|
||||
|
||||
boss.ApplyShield(amount, 30f);
|
||||
Debug.Log($"[Debug] 보스 보호막 적용 | Amount={amount:F0} | Total={boss.Shield:F0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/_Game/Scripts/Editor/DebugBossMenuItems.cs.meta
Normal file
2
Assets/_Game/Scripts/Editor/DebugBossMenuItems.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b710fae394e79d44fa88104e3412c04a
|
||||
Reference in New Issue
Block a user