using UnityEngine; using System.Collections; public class NorcielBoss : MonsterClass { [Header("--- 🧠 두뇌 연결 ---")] [SerializeField] private BossCounterSystem counterSystem; [Header("--- ⚔️ 패턴 설정 ---")] [SerializeField] private float patternInterval = 3f; // 공격 간격 [Header("--- 📊 UI 연결 ---")] [SerializeField] private GameObject bossHealthBar; // 보스 체력바 UI (평소엔 꺼둠) // 내부 변수 private float _timer; private Rigidbody rb; private bool isBattleStarted = false; // 전투 시작 여부 체크 private Transform target; // 플레이어 타겟 protected override void Awake() { base.Awake(); rb = GetComponent(); } protected override void Init() { // 1. 기본 변수 초기화 _timer = patternInterval; // 플레이어 찾기 (Tag: Player) GameObject playerObj = GameObject.FindWithTag("Player"); if (playerObj != null) { target = playerObj.transform; } else { var playerScript = FindObjectOfType(); if (playerScript != null) target = playerScript.transform; } // 2. 전투 시작 전 상태 설정 (봉인) isBattleStarted = false; // 움직임 끄기 if (agent != null) { agent.enabled = false; agent.isStopped = true; } // 체력바 숨기기 if (bossHealthBar != null) bossHealthBar.SetActive(false); } protected override void ExecuteAILogic() { // ⭐ 전투 미시작 or 타겟 없음 -> 정지 if (!isBattleStarted || target == null) return; // 1. 공격 쿨타임 체크 _timer -= Time.deltaTime; // 쿨타임 끝 + 공격중 아님 + 피격중 아님 + 살아있음 -> 공격 시도 if (_timer <= 0 && !isAttacking && !isHit && !isDead) { _timer = patternInterval; DecideAttack(); } // 2. 이동 및 애니메이션 처리 (평소 상태) if (!isAttacking && agent.enabled) { agent.SetDestination(target.position); // ──────── ⭐ [핵심] 이동 애니메이션 동기화 ──────── if (animator != null) { // 현재 이동 속도를 가져와서 애니메이터에 전달 (0 ~ 3.5 ~ ...) // Animator의 Blend Tree가 이 값을 받아 Idle/Walk/Run을 섞어줌 float currentSpeed = agent.velocity.magnitude; animator.SetFloat("Speed", currentSpeed); } // ────────────────────────────────────────────── // 사거리 안으로 들어왔거나 너무 가까우면 멈춤 if (agent.remainingDistance <= agent.stoppingDistance) { agent.isStopped = true; // 몸을 플레이어 쪽으로 돌리기 Vector3 dir = target.position - transform.position; dir.y = 0; if (dir != Vector3.zero) { transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 5f); } } else { agent.isStopped = false; } } } // ════════════════════════════════════════ // 🎬 전투 입장 시스템 (외부 호출용) // ════════════════════════════════════════ public void StartBossBattle() { if (isBattleStarted) return; StartCoroutine(BattleStartRoutine()); } private IEnumerator BattleStartRoutine() { // 포효 (스트링 방식) if (animator != null) animator.Play("Roar"); Debug.Log("😈 보스: 감히 내 영역에 들어오다니! (전투 시작)"); if (bossHealthBar != null) bossHealthBar.SetActive(true); if (counterSystem != null) counterSystem.InitializeBattle(); yield return new WaitForSeconds(2.0f); // 포효 끝날 때까지 대기 isBattleStarted = true; if (agent != null) agent.enabled = true; } // ════════════════════════════════════════ // 🧠 AI 판단 및 공격 실행 // ════════════════════════════════════════ private void DecideAttack() { // 이동 애니메이션 잠시 0으로 (미끄러짐 방지) if (animator != null) animator.SetFloat("Speed", 0f); string patternName = (counterSystem != null) ? counterSystem.SelectBossPattern() : "Normal"; Debug.Log($"🤖 보스 AI 결정: {patternName}"); switch (patternName) { case "DashAttack": StartCoroutine(Pattern_DashAttack()); break; case "Smash": StartCoroutine(Pattern_SmashAttack()); break; case "ShieldWall": StartCoroutine(Pattern_ShieldWall()); break; default: StartCoroutine(Pattern_NormalShoot()); break; } } // ════════════════════════════════════════ // ⚔️ 공격 패턴 코루틴 (스트링 방식) // ════════════════════════════════════════ private IEnumerator Pattern_DashAttack() { OnAttackStart(); if (animator != null) animator.Play("Skill_Dash_Ready"); yield return new WaitForSeconds(0.5f); // 돌진 (물리 이동) if (agent != null) agent.enabled = false; if (rb != null) { rb.isKinematic = false; rb.velocity = transform.forward * 20f; } if (animator != null) animator.Play("Skill_Dash_Go"); yield return new WaitForSeconds(1.0f); // 복구 if (rb != null) { rb.velocity = Vector3.zero; rb.isKinematic = true; } if (agent != null && isBattleStarted) agent.enabled = true; OnAttackEnd(); } private IEnumerator Pattern_SmashAttack() { OnAttackStart(); if (animator != null) animator.Play("Skill_Smash_Charge"); yield return new WaitForSeconds(1.2f); if (animator != null) animator.Play("Skill_Smash_Impact"); yield return new WaitForSeconds(1.0f); OnAttackEnd(); } private IEnumerator Pattern_ShieldWall() { OnAttackStart(); if (animator != null) animator.Play("Skill_Shield"); yield return new WaitForSeconds(2.0f); OnAttackEnd(); } private IEnumerator Pattern_NormalShoot() { OnAttackStart(); if (animator != null) animator.Play("Attack_Shoot"); yield return new WaitForSeconds(1.0f); OnAttackEnd(); } }