diff --git a/Assets/Scripts/Editor/BossEnemyEditor.cs b/Assets/Scripts/Editor/BossEnemyEditor.cs
new file mode 100644
index 00000000..42b8e458
--- /dev/null
+++ b/Assets/Scripts/Editor/BossEnemyEditor.cs
@@ -0,0 +1,314 @@
+#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 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);
+
+ // 디버그 도구
+ 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 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
diff --git a/Assets/Scripts/Editor/BossEnemyEditor.cs.meta b/Assets/Scripts/Editor/BossEnemyEditor.cs.meta
new file mode 100644
index 00000000..e309321d
--- /dev/null
+++ b/Assets/Scripts/Editor/BossEnemyEditor.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 867ffff975b9a7a4694783f4a5ee1c6e
\ No newline at end of file
diff --git a/Assets/Scripts/Enemy/BossEnemy.cs b/Assets/Scripts/Enemy/BossEnemy.cs
new file mode 100644
index 00000000..4a5431d8
--- /dev/null
+++ b/Assets/Scripts/Enemy/BossEnemy.cs
@@ -0,0 +1,291 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using Unity.Netcode;
+using Unity.Behavior;
+using Colosseum.Stats;
+
+namespace Colosseum.Enemy
+{
+ ///
+ /// 보스 캐릭터. 페이즈 시스템과 동적 AI 전환을 지원합니다.
+ /// Unity Behavior 패키지를 사용하여 Behavior Tree 기반 AI를 구현합니다.
+ ///
+ public class BossEnemy : EnemyBase
+ {
+ [Header("Boss Settings")]
+ [Tooltip("보스 페이즈 데이터 목록 (순서대로 전환)")]
+ [SerializeField] private List phases = new();
+
+ [Tooltip("초기 Behavior Graph")]
+ [SerializeField] private BehaviorGraph initialBehaviorGraph;
+
+ [Header("Phase Settings")]
+ [Tooltip("페이즈 전환 시 무적 시간")]
+ [Min(0f)] [SerializeField] private float phaseTransitionInvincibilityTime = 2f;
+
+ [Tooltip("페이즈 전환 연출 시간")]
+ [Min(0f)] [SerializeField] private float phaseTransitionDuration = 3f;
+
+ [Header("Debug")]
+ [SerializeField] private bool debugMode = true;
+
+ // 컴포넌트
+ private BehaviorGraphAgent behaviorAgent;
+
+ // 페이즈 상태
+ private int currentPhaseIndex = 0;
+ private bool isTransitioning = false;
+ private float phaseStartTime;
+ private float phaseElapsedTime;
+ private bool isInvincible = false;
+
+ // 커스텀 조건 딕셔너리
+ private Dictionary customConditions = new Dictionary();
+
+ // 이벤트
+ public event System.Action OnPhaseChanged; // phaseIndex
+ public event System.Action OnPhaseTransitionStart; // transitionDuration
+ public event System.Action OnPhaseTransitionEnd;
+
+ // Properties
+ public int CurrentPhaseIndex => currentPhaseIndex;
+ public BossPhaseData CurrentPhase => phases.Count > currentPhaseIndex ? phases[currentPhaseIndex] : null;
+ public int TotalPhases => phases.Count;
+ public bool IsTransitioning => isTransitioning;
+ public float PhaseElapsedTime => phaseElapsedTime;
+
+ public override void OnNetworkSpawn()
+ {
+ base.OnNetworkSpawn();
+
+ // BehaviorGraphAgent 컴포넌트 확인/추가
+ behaviorAgent = GetComponent();
+ if (behaviorAgent == null)
+ {
+ behaviorAgent = gameObject.AddComponent();
+ }
+
+ // 초기 AI 설정
+ if (IsServer && initialBehaviorGraph != null)
+ {
+ behaviorAgent.Graph = initialBehaviorGraph;
+ }
+ }
+
+ protected override void InitializeStats()
+ {
+ base.InitializeStats();
+ phaseStartTime = Time.time;
+ phaseElapsedTime = 0f;
+ currentPhaseIndex = 0;
+ isTransitioning = false;
+ isInvincible = false;
+ customConditions.Clear();
+ }
+
+ private void Update()
+ {
+ if (!IsServer || IsDead || isTransitioning)
+ return;
+
+ phaseElapsedTime = Time.time - phaseStartTime;
+
+ // 다음 페이즈 전환 조건 확인
+ CheckPhaseTransition();
+ }
+
+ ///
+ /// 페이즈 전환 조건 확인
+ ///
+ private void CheckPhaseTransition()
+ {
+ int nextPhaseIndex = currentPhaseIndex + 1;
+ if (nextPhaseIndex >= phases.Count)
+ return;
+
+ BossPhaseData nextPhase = phases[nextPhaseIndex];
+ if (nextPhase == null)
+ return;
+
+ if (nextPhase.CheckTransitionCondition(this, phaseElapsedTime))
+ {
+ StartPhaseTransition(nextPhaseIndex);
+ }
+ }
+
+ ///
+ /// 페이즈 전환 시작
+ ///
+ private void StartPhaseTransition(int newPhaseIndex)
+ {
+ if (newPhaseIndex >= phases.Count || isTransitioning)
+ return;
+
+ isTransitioning = true;
+ isInvincible = true;
+
+ StartCoroutine(PhaseTransitionCoroutine(newPhaseIndex));
+ }
+
+ private System.Collections.IEnumerator PhaseTransitionCoroutine(int newPhaseIndex)
+ {
+ BossPhaseData newPhase = phases[newPhaseIndex];
+
+ // 전환 이벤트
+ OnPhaseTransitionStart?.Invoke(phaseTransitionDuration);
+
+ // 전환 연출
+ yield return PlayPhaseTransitionEffect(newPhase);
+
+ // AI 그래프 교체
+ if (newPhase.BehaviorGraph != null && behaviorAgent != null)
+ {
+ behaviorAgent.End();
+ behaviorAgent.Graph = newPhase.BehaviorGraph;
+ }
+
+ // 페이즈 전환 완료
+ currentPhaseIndex = newPhaseIndex;
+ phaseStartTime = Time.time;
+ phaseElapsedTime = 0f;
+
+ // 무적 해제
+ yield return new WaitForSeconds(phaseTransitionInvincibilityTime);
+ isInvincible = false;
+ isTransitioning = false;
+
+ OnPhaseTransitionEnd?.Invoke();
+ OnPhaseChanged?.Invoke(currentPhaseIndex);
+
+ if (debugMode)
+ {
+ Debug.Log($"[Boss] Phase transition: {currentPhaseIndex} ({newPhase.PhaseName})");
+ }
+ }
+
+ ///
+ /// 페이즈 전환 연출 재생
+ ///
+ private System.Collections.IEnumerator PlayPhaseTransitionEffect(BossPhaseData newPhase)
+ {
+ // 애니메이션 재생
+ if (animator != null && newPhase.PhaseStartAnimation != null)
+ {
+ animator.Play(newPhase.PhaseStartAnimation.name);
+ }
+
+ // 이펙트 생성
+ if (newPhase.PhaseTransitionEffect != null)
+ {
+ var effect = Instantiate(newPhase.PhaseTransitionEffect, transform.position, transform.rotation);
+ Destroy(effect, phaseTransitionDuration);
+ }
+
+ // 전환 시간 대기
+ yield return new WaitForSeconds(phaseTransitionDuration);
+ }
+
+ ///
+ /// 대미지 적용 (무적 상태 고려)
+ ///
+ public override float TakeDamage(float damage, object source = null)
+ {
+ if (isInvincible)
+ return 0f;
+
+ return base.TakeDamage(damage, source);
+ }
+
+ ///
+ /// 커스텀 조건 설정
+ ///
+ public void SetCustomCondition(string conditionId, bool value)
+ {
+ customConditions[conditionId] = value;
+ }
+
+ ///
+ /// 커스텀 조건 확인
+ ///
+ public bool CheckCustomCondition(string conditionId)
+ {
+ return customConditions.TryGetValue(conditionId, out bool value) && value;
+ }
+
+ ///
+ /// 수동으로 페이즈 전환
+ ///
+ public void ForcePhaseTransition(int phaseIndex)
+ {
+ if (!IsServer)
+ return;
+
+ if (phaseIndex >= 0 && phaseIndex < phases.Count && phaseIndex != currentPhaseIndex)
+ {
+ StartPhaseTransition(phaseIndex);
+ }
+ }
+
+ ///
+ /// 현재 페이즈 재시작
+ ///
+ public void RestartCurrentPhase()
+ {
+ if (!IsServer)
+ return;
+
+ phaseStartTime = Time.time;
+ phaseElapsedTime = 0f;
+
+ if (behaviorAgent != null)
+ {
+ behaviorAgent.Restart();
+ }
+ }
+
+ protected override void HandleDeath()
+ {
+ // 마지막 페이즈에서만 사망 처리
+ if (currentPhaseIndex < phases.Count - 1 && !isTransitioning)
+ {
+ // 아직 페이즈가 남아있으면 강제로 다음 페이즈로
+ StartPhaseTransition(currentPhaseIndex + 1);
+ return;
+ }
+
+ base.HandleDeath();
+
+ // AI 정지
+ if (behaviorAgent != null)
+ {
+ behaviorAgent.End();
+ }
+ }
+
+ #region Debug
+
+ private void OnDrawGizmosSelected()
+ {
+ if (!debugMode)
+ return;
+
+ // 현재 페이즈 정보 표시
+ #if UNITY_EDITOR
+ if (phases != null && currentPhaseIndex < phases.Count)
+ {
+ var phase = phases[currentPhaseIndex];
+ if (phase != null)
+ {
+ UnityEditor.Handles.Label(
+ transform.position + Vector3.up * 3f,
+ $"Phase {currentPhaseIndex + 1}/{phases.Count}\n{phase.PhaseName}"
+ );
+ }
+ }
+ #endif
+ }
+
+ #endregion
+ }
+}
diff --git a/Assets/Scripts/Enemy/BossEnemy.cs.meta b/Assets/Scripts/Enemy/BossEnemy.cs.meta
new file mode 100644
index 00000000..bfae6985
--- /dev/null
+++ b/Assets/Scripts/Enemy/BossEnemy.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 4a49d1cf004a0c944be905fe6fabf936
\ No newline at end of file