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

322 lines
11 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; // 공격 간격
2026-02-11 07:21:58 +00:00
[Header("--- 🎱 무기(쇠공) 설정 (필수!) ---")]
[SerializeField] private GameObject ironBall; // 씬에 있는 실제 쇠공 오브젝트
[SerializeField] private Transform handHolder; // 쇠공이 붙어있을 손의 위치 (RightHand 뼈)
2026-02-10 15:29:22 +00:00
[Header("--- 📊 UI 연결 ---")]
2026-02-11 07:21:58 +00:00
[SerializeField] private GameObject bossHealthBar; // 보스 체력바 UI
2026-02-10 15:29:22 +00:00
2026-02-11 07:21:58 +00:00
// --- 내부 변수들 ---
2026-02-10 15:29:22 +00:00
private float _timer;
2026-02-11 07:21:58 +00:00
private Rigidbody rb; // 보스 본체의 리지드바디
private Rigidbody ballRb; // 쇠공의 리지드바디
private bool isBattleStarted = false; // 전투 시작 여부
private bool isWeaponless = false; // 현재 무기가 없는가?
private Transform target; // 플레이어 타겟
2026-02-10 15:29:22 +00:00
protected override void Awake()
{
base.Awake();
rb = GetComponent<Rigidbody>();
2026-02-11 07:21:58 +00:00
// 쇠공에서 리지드바디 가져오기
if (ironBall != null) ballRb = ironBall.GetComponent<Rigidbody>();
2026-02-10 15:29:22 +00:00
}
protected override void Init()
{
2026-02-11 07:21:58 +00:00
// 1. 변수 초기화
2026-02-10 15:29:22 +00:00
_timer = patternInterval;
2026-02-11 07:21:58 +00:00
isBattleStarted = false;
isWeaponless = false;
2026-02-10 15:29:22 +00:00
2026-02-11 07:21:58 +00:00
// 2. 플레이어 찾기
2026-02-10 15:29:22 +00:00
GameObject playerObj = GameObject.FindWithTag("Player");
2026-02-11 07:21:58 +00:00
if (playerObj != null) target = playerObj.transform;
2026-02-10 15:29:22 +00:00
else
{
var playerScript = FindObjectOfType<PlayerMovement>();
if (playerScript != null) target = playerScript.transform;
}
2026-02-11 07:21:58 +00:00
// 3. 상태 봉인 (움직임 끄기)
2026-02-10 15:29:22 +00:00
if (agent != null)
{
agent.enabled = false;
agent.isStopped = true;
}
if (bossHealthBar != null) bossHealthBar.SetActive(false);
2026-02-11 07:21:58 +00:00
// 4. 쇠공 초기 상태 설정 (물리 끄고 손에 붙이기)
if (ballRb != null) ballRb.isKinematic = true;
2026-02-10 15:29:22 +00:00
}
protected override void ExecuteAILogic()
{
2026-02-11 07:21:58 +00:00
// 전투 미시작 or 타겟 없음 -> 정지
2026-02-10 15:29:22 +00:00
if (!isBattleStarted || target == null) return;
2026-02-11 07:21:58 +00:00
// ⭐ [최우선 순위] 무기가 없으면 공격 중단하고 주으러 감
if (isWeaponless)
{
RetrieveWeaponLogic();
return;
}
// --- 일반 전투 로직 ---
2026-02-10 15:29:22 +00:00
// 1. 공격 쿨타임 체크
_timer -= Time.deltaTime;
if (_timer <= 0 && !isAttacking && !isHit && !isDead)
{
_timer = patternInterval;
DecideAttack();
}
2026-02-11 07:21:58 +00:00
// 2. 평소 이동 (플레이어 추격)
2026-02-10 15:29:22 +00:00
if (!isAttacking && agent.enabled)
{
agent.SetDestination(target.position);
2026-02-11 07:21:58 +00:00
// 애니메이션 속도 동기화
if (animator != null) animator.SetFloat("Speed", agent.velocity.magnitude);
2026-02-10 15:29:22 +00:00
2026-02-11 07:21:58 +00:00
// 거리 체크
2026-02-10 15:29:22 +00:00
if (agent.remainingDistance <= agent.stoppingDistance)
{
agent.isStopped = true;
2026-02-11 07:21:58 +00:00
LookAtTarget(target.position);
2026-02-10 15:29:22 +00:00
}
else
{
agent.isStopped = false;
}
}
}
// ════════════════════════════════════════
2026-02-11 07:21:58 +00:00
// 🏃‍♂️ 무기 회수 로직 (Retrieve System)
// ════════════════════════════════════════
private void RetrieveWeaponLogic()
{
if (ironBall == null) return;
// 1. 공 위치로 이동
if (agent.enabled)
{
agent.SetDestination(ironBall.transform.position);
agent.isStopped = false;
}
// 이동 애니메이션
if (animator != null) animator.SetFloat("Speed", agent.velocity.magnitude);
// 2. 공과의 거리 체크 (가까워지면 줍기 시도)
float dist = Vector3.Distance(transform.position, ironBall.transform.position);
// 거리 3.0f 이내이고, 줍는 중이 아닐 때 실행
if (dist <= 3.0f && !isAttacking)
{
StartCoroutine(PickUpBallRoutine());
}
}
private IEnumerator PickUpBallRoutine()
{
OnAttackStart(); // 행동 시작 (다른 행동 불가)
// 멈춤
if (agent != null)
{
agent.isStopped = true;
agent.velocity = Vector3.zero;
}
if (animator != null) animator.SetFloat("Speed", 0);
// 1. 줍는 모션 재생
Debug.Log("boss: 내 소중한 공!!");
if (animator != null) animator.Play("Skill_Pickup"); // 줍는 애니메이션
yield return new WaitForSeconds(0.8f); // 손이 바닥에 닿을 때까지 대기
// 2. ⭐ 공을 다시 손에 붙이기 (Parent)
ironBall.transform.SetParent(handHolder);
// 위치 초기화 (손바닥 안으로)
ironBall.transform.localPosition = Vector3.zero;
ironBall.transform.localRotation = Quaternion.identity;
// 쇠공 물리 끄기 (다시 무기화)
if (ballRb != null)
{
ballRb.isKinematic = true;
ballRb.velocity = Vector3.zero;
}
yield return new WaitForSeconds(1.0f); // 일어나는 시간
// 3. 상태 복구
isWeaponless = false; // 무기 장착 완료!
OnAttackEnd();
if (agent != null) agent.isStopped = false;
}
// ════════════════════════════════════════
// 🎬 전투 입장 & 유틸리티
2026-02-10 15:29:22 +00:00
// ════════════════════════════════════════
public void StartBossBattle()
{
if (isBattleStarted) return;
StartCoroutine(BattleStartRoutine());
}
private IEnumerator BattleStartRoutine()
{
if (animator != null) animator.Play("Roar");
if (bossHealthBar != null) bossHealthBar.SetActive(true);
if (counterSystem != null) counterSystem.InitializeBattle();
2026-02-11 07:21:58 +00:00
yield return new WaitForSeconds(2.0f);
2026-02-10 15:29:22 +00:00
isBattleStarted = true;
if (agent != null) agent.enabled = true;
}
2026-02-11 07:21:58 +00:00
private void LookAtTarget(Vector3 targetPos)
{
Vector3 dir = targetPos - transform.position;
dir.y = 0;
if (dir != Vector3.zero)
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 5f);
}
}
2026-02-10 15:29:22 +00:00
// ════════════════════════════════════════
2026-02-11 07:21:58 +00:00
// 🧠 AI 공격 판단
2026-02-10 15:29:22 +00:00
// ════════════════════════════════════════
private void DecideAttack()
{
2026-02-11 07:21:58 +00:00
// 이동 애니메이션 잠시 0으로
2026-02-10 15:29:22 +00:00
if (animator != null) animator.SetFloat("Speed", 0f);
string patternName = (counterSystem != null) ? counterSystem.SelectBossPattern() : "Normal";
switch (patternName)
{
2026-02-11 07:21:58 +00:00
case "DashAttack": StartCoroutine(Pattern_DashAttack()); break;
case "Smash": StartCoroutine(Pattern_SmashAttack()); break;
case "ShieldWall": StartCoroutine(Pattern_ShieldWall()); break;
default: StartCoroutine(Pattern_ThrowBall()); break; // 기본 공격을 던지기로 대체
2026-02-10 15:29:22 +00:00
}
}
// ════════════════════════════════════════
2026-02-11 07:21:58 +00:00
// ⚔️ 공격 패턴 (쇠공 버전)
2026-02-10 15:29:22 +00:00
// ════════════════════════════════════════
2026-02-11 07:21:58 +00:00
// 1. 탱크 돌격 (Dash)
2026-02-10 15:29:22 +00:00
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);
2026-02-11 07:21:58 +00:00
// 정지
2026-02-10 15:29:22 +00:00
if (rb != null)
{
rb.velocity = Vector3.zero;
rb.isKinematic = true;
}
if (agent != null && isBattleStarted) agent.enabled = true;
OnAttackEnd();
}
2026-02-11 07:21:58 +00:00
// 2. 메테오 스매시 (Smash)
2026-02-10 15:29:22 +00:00
private IEnumerator Pattern_SmashAttack()
{
OnAttackStart();
2026-02-11 07:21:58 +00:00
// 엇박자 위협 (기 모으기)
2026-02-10 15:29:22 +00:00
if (animator != null) animator.Play("Skill_Smash_Charge");
yield return new WaitForSeconds(1.2f);
2026-02-11 07:21:58 +00:00
// 쾅!
2026-02-10 15:29:22 +00:00
if (animator != null) animator.Play("Skill_Smash_Impact");
yield return new WaitForSeconds(1.0f);
OnAttackEnd();
}
2026-02-11 07:21:58 +00:00
// 3. 쇠공 풍차 돌리기 (Shield)
2026-02-10 15:29:22 +00:00
private IEnumerator Pattern_ShieldWall()
{
OnAttackStart();
2026-02-11 07:21:58 +00:00
// 쇠공을 빙빙 돌림 (화살 튕겨내기)
2026-02-10 15:29:22 +00:00
if (animator != null) animator.Play("Skill_Shield");
yield return new WaitForSeconds(2.0f);
OnAttackEnd();
}
2026-02-11 07:21:58 +00:00
// 4. 공 던지기 (Throw - 기존 Shoot 대체)
private IEnumerator Pattern_ThrowBall()
2026-02-10 15:29:22 +00:00
{
OnAttackStart();
2026-02-11 07:21:58 +00:00
// 던지는 모션
if (animator != null) animator.Play("Attack_Throw");
yield return new WaitForSeconds(0.5f); // 손이 앞으로 뻗는 타이밍
// ⭐ 공 발사 로직
if (ironBall != null)
{
// 1. 손에서 떼어내기 (부모 해제)
ironBall.transform.SetParent(null);
// 2. 물리 켜고 날리기
if (ballRb != null)
{
ballRb.isKinematic = false;
// 플레이어 방향 계산
Vector3 dir = (target.position - transform.position).normalized;
// 위쪽으로 살짝 띄워서 포물선으로 던짐
ballRb.AddForce(dir * 1000f + Vector3.up * 300f);
// 굴러가지 않게 저항 좀 주기
ballRb.angularDrag = 5f;
}
}
// "나 무기 없다!" 상태 설정 -> 이제부터 주으러 감
isWeaponless = true;
2026-02-10 15:29:22 +00:00
yield return new WaitForSeconds(1.0f);
OnAttackEnd();
}
}