Projext/Assets/Scripts/Enemy/BossAI/BossMonster.cs

222 lines
7.3 KiB
C#
Raw Normal View History

2026-02-10 15:29:22 +00:00
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<Rigidbody>();
}
protected override void Init()
{
// 1. 기본 변수 초기화
_timer = patternInterval;
// 플레이어 찾기 (Tag: Player)
GameObject playerObj = GameObject.FindWithTag("Player");
if (playerObj != null)
{
target = playerObj.transform;
}
else
{
var playerScript = FindObjectOfType<PlayerMovement>();
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();
}
}