2026-02-10 15:29:22 +00:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
|
|
|
|
|
|
public class NorcielBoss : MonsterClass
|
|
|
|
|
|
{
|
|
|
|
|
|
[Header("--- 🧠 두뇌 연결 ---")]
|
|
|
|
|
|
[SerializeField] private BossCounterSystem counterSystem;
|
|
|
|
|
|
|
|
|
|
|
|
[Header("--- ⚔️ 패턴 설정 ---")]
|
2026-02-11 13:47:53 +00:00
|
|
|
|
[SerializeField] private float patternInterval = 3f;
|
2026-02-10 15:29:22 +00:00
|
|
|
|
|
2026-02-11 07:21:58 +00:00
|
|
|
|
[Header("--- 🎱 무기(쇠공) 설정 (필수!) ---")]
|
2026-02-11 13:47:53 +00:00
|
|
|
|
[SerializeField] private GameObject ironBall;
|
|
|
|
|
|
[SerializeField] private Transform handHolder;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
[Header("--- 📊 UI 연결 ---")]
|
2026-02-11 13:47:53 +00:00
|
|
|
|
[SerializeField] private GameObject bossHealthBar;
|
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 13:47:53 +00:00
|
|
|
|
private Rigidbody rb;
|
|
|
|
|
|
private Rigidbody ballRb;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
private bool isBattleStarted = false;
|
|
|
|
|
|
private bool isWeaponless = false;
|
|
|
|
|
|
private bool isPerformingAction = false; // ⭐ 신규: 연출 중 플래그 (Roar 등)
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// ⭐ [수정] Start → OnEnable 이후 호출되도록 변경
|
|
|
|
|
|
private void Start()
|
|
|
|
|
|
{
|
|
|
|
|
|
StartBossBattle();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
protected override void Init()
|
|
|
|
|
|
{
|
|
|
|
|
|
_timer = patternInterval;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
isBattleStarted = false;
|
|
|
|
|
|
isWeaponless = false;
|
2026-02-11 13:47:53 +00:00
|
|
|
|
isPerformingAction = false;
|
2026-02-10 15:29:22 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 플레이어 찾기
|
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 13:47:53 +00:00
|
|
|
|
// HP 초기화
|
|
|
|
|
|
currentHP = maxHP;
|
|
|
|
|
|
|
|
|
|
|
|
StartCoroutine(SafeInitNavMesh());
|
|
|
|
|
|
|
|
|
|
|
|
if (bossHealthBar != null) bossHealthBar.SetActive(false);
|
|
|
|
|
|
if (ballRb != null) ballRb.isKinematic = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private IEnumerator SafeInitNavMesh()
|
|
|
|
|
|
{
|
|
|
|
|
|
yield return null;
|
|
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
if (agent != null)
|
|
|
|
|
|
{
|
2026-02-11 13:47:53 +00:00
|
|
|
|
int retry = 0;
|
|
|
|
|
|
while (!agent.isOnNavMesh && retry < 5)
|
|
|
|
|
|
{
|
|
|
|
|
|
retry++;
|
|
|
|
|
|
yield return new WaitForSeconds(0.1f);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (agent.isOnNavMesh)
|
|
|
|
|
|
{
|
|
|
|
|
|
agent.isStopped = true;
|
|
|
|
|
|
}
|
2026-02-10 15:29:22 +00:00
|
|
|
|
agent.enabled = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// ════════════════════════════════════════
|
|
|
|
|
|
// ⭐ [핵심 수정] 부모의 OnManagedUpdate 흐름 제어
|
|
|
|
|
|
// ════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 부모 MonsterClass의 StopMovement()가 매 프레임 Monster_Idle을
|
|
|
|
|
|
/// 강제 재생해서 Roar/다른 애니메이션을 덮어쓰는 문제 해결.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 보스는 일반 몬스터와 다르게:
|
|
|
|
|
|
/// - 전투 시작 전에는 아무 AI도 돌리지 않음
|
|
|
|
|
|
/// - 연출 중(Roar 등)에는 애니메이션을 건드리지 않음
|
|
|
|
|
|
/// </summary>
|
2026-02-10 15:29:22 +00:00
|
|
|
|
protected override void ExecuteAILogic()
|
|
|
|
|
|
{
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 연출 중이거나 전투 미시작 → 아무것도 안 함
|
|
|
|
|
|
if (isPerformingAction || !isBattleStarted || target == null) return;
|
2026-02-10 15:29:22 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 무기 없으면 회수 우선
|
2026-02-11 07:21:58 +00:00
|
|
|
|
if (isWeaponless)
|
|
|
|
|
|
{
|
|
|
|
|
|
RetrieveWeaponLogic();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- 일반 전투 로직 ---
|
2026-02-10 15:29:22 +00:00
|
|
|
|
_timer -= Time.deltaTime;
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (_timer <= 0 && !isAttacking && !isHit && !isDead && !isResting)
|
2026-02-10 15:29:22 +00:00
|
|
|
|
{
|
|
|
|
|
|
_timer = patternInterval;
|
|
|
|
|
|
DecideAttack();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 이동 (공격/피격 중이 아닐 때만)
|
|
|
|
|
|
if (!isAttacking && !isHit && !isResting && agent != null && agent.enabled && agent.isOnNavMesh)
|
2026-02-10 15:29:22 +00:00
|
|
|
|
{
|
|
|
|
|
|
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 13:47:53 +00:00
|
|
|
|
if (agent.remainingDistance <= agent.stoppingDistance + 0.5f)
|
2026-02-10 15:29:22 +00:00
|
|
|
|
{
|
|
|
|
|
|
agent.isStopped = true;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
LookAtTarget(target.position);
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (animator != null) animator.SetFloat("Speed", 0f);
|
2026-02-10 15:29:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
agent.isStopped = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ════════════════════════════════════════
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 🏃♂️ 무기 회수 로직
|
2026-02-11 07:21:58 +00:00
|
|
|
|
// ════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
|
|
private void RetrieveWeaponLogic()
|
|
|
|
|
|
{
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (ironBall == null || !agent.enabled || !agent.isOnNavMesh) return;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
agent.SetDestination(ironBall.transform.position);
|
|
|
|
|
|
agent.isStopped = false;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
|
|
|
|
|
if (animator != null) animator.SetFloat("Speed", agent.velocity.magnitude);
|
|
|
|
|
|
|
|
|
|
|
|
float dist = Vector3.Distance(transform.position, ironBall.transform.position);
|
|
|
|
|
|
|
|
|
|
|
|
if (dist <= 3.0f && !isAttacking)
|
|
|
|
|
|
{
|
|
|
|
|
|
StartCoroutine(PickUpBallRoutine());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private IEnumerator PickUpBallRoutine()
|
|
|
|
|
|
{
|
2026-02-11 13:47:53 +00:00
|
|
|
|
OnAttackStart();
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (agent != null && agent.isOnNavMesh)
|
2026-02-11 07:21:58 +00:00
|
|
|
|
{
|
|
|
|
|
|
agent.isStopped = true;
|
|
|
|
|
|
agent.velocity = Vector3.zero;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (animator != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
animator.SetFloat("Speed", 0);
|
|
|
|
|
|
animator.Play("Skill_Pickup");
|
|
|
|
|
|
}
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
yield return new WaitForSeconds(0.8f);
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
|
|
|
|
|
ironBall.transform.SetParent(handHolder);
|
|
|
|
|
|
ironBall.transform.localPosition = Vector3.zero;
|
|
|
|
|
|
ironBall.transform.localRotation = Quaternion.identity;
|
|
|
|
|
|
|
|
|
|
|
|
if (ballRb != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ballRb.isKinematic = true;
|
|
|
|
|
|
ballRb.velocity = Vector3.zero;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
yield return new WaitForSeconds(1.0f);
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
isWeaponless = false;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
OnAttackEnd();
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (agent != null && agent.isOnNavMesh) agent.isStopped = false;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ════════════════════════════════════════
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 🎬 전투 입장
|
2026-02-10 15:29:22 +00:00
|
|
|
|
// ════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
|
|
public void StartBossBattle()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isBattleStarted) return;
|
|
|
|
|
|
StartCoroutine(BattleStartRoutine());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private IEnumerator BattleStartRoutine()
|
|
|
|
|
|
{
|
2026-02-11 13:47:53 +00:00
|
|
|
|
Debug.Log("🔥 보스 전투 시작! (Roar)");
|
|
|
|
|
|
|
|
|
|
|
|
// ⭐ [핵심] 연출 중 플래그 ON → ExecuteAILogic이 아무것도 안 함
|
|
|
|
|
|
isPerformingAction = true;
|
|
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
if (bossHealthBar != null) bossHealthBar.SetActive(true);
|
|
|
|
|
|
if (counterSystem != null) counterSystem.InitializeBattle();
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// ⭐ [핵심] Roar 재생
|
|
|
|
|
|
if (animator != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
animator.Play("Roar", 0, 0f);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Roar 애니메이션 대기 (길이에 맞게 조절)
|
2026-02-11 07:21:58 +00:00
|
|
|
|
yield return new WaitForSeconds(2.0f);
|
2026-02-10 15:29:22 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// ⭐ [핵심] Roar 끝나면 Idle로 전환
|
|
|
|
|
|
if (animator != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
animator.Play("Monster_Idle", 0, 0f);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 연출 종료 → 전투 시작
|
|
|
|
|
|
isPerformingAction = false;
|
2026-02-10 15:29:22 +00:00
|
|
|
|
isBattleStarted = true;
|
2026-02-11 13:47:53 +00:00
|
|
|
|
|
|
|
|
|
|
// NavMeshAgent 활성화
|
|
|
|
|
|
if (agent != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
agent.enabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
// NavMesh 위에 올라갈 때까지 잠시 대기
|
|
|
|
|
|
int retry = 0;
|
|
|
|
|
|
while (!agent.isOnNavMesh && retry < 10)
|
|
|
|
|
|
{
|
|
|
|
|
|
retry++;
|
|
|
|
|
|
yield return new WaitForSeconds(0.1f);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (agent.isOnNavMesh)
|
|
|
|
|
|
{
|
|
|
|
|
|
agent.isStopped = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log("⚔️ 보스 전투 루프 시작!");
|
2026-02-10 15:29:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2026-02-11 13:47:53 +00:00
|
|
|
|
transform.rotation = Quaternion.Slerp(
|
|
|
|
|
|
transform.rotation,
|
|
|
|
|
|
Quaternion.LookRotation(dir),
|
|
|
|
|
|
Time.deltaTime * 5f
|
|
|
|
|
|
);
|
2026-02-11 07:21:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (animator != null) animator.SetFloat("Speed", 0f);
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 공격 전 이동 멈춤
|
|
|
|
|
|
if (agent != null && agent.isOnNavMesh)
|
|
|
|
|
|
{
|
|
|
|
|
|
agent.isStopped = true;
|
|
|
|
|
|
agent.velocity = Vector3.zero;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 플레이어를 바라봄
|
|
|
|
|
|
if (target != null) LookAtTarget(target.position);
|
|
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
string patternName = (counterSystem != null) ? counterSystem.SelectBossPattern() : "Normal";
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
Debug.Log($"🧠 보스 패턴 선택: {patternName}");
|
|
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
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;
|
2026-02-11 13:47:53 +00:00
|
|
|
|
default: StartCoroutine(Pattern_ThrowBall()); break;
|
2026-02-10 15:29:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ════════════════════════════════════════
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// ⚔️ 공격 패턴
|
2026-02-10 15:29:22 +00:00
|
|
|
|
// ════════════════════════════════════════
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 1. 돌진
|
2026-02-10 15:29:22 +00:00
|
|
|
|
private IEnumerator Pattern_DashAttack()
|
|
|
|
|
|
{
|
|
|
|
|
|
OnAttackStart();
|
|
|
|
|
|
|
|
|
|
|
|
if (agent != null) agent.enabled = false;
|
2026-02-11 13:47:53 +00:00
|
|
|
|
|
|
|
|
|
|
if (animator != null) animator.Play("Skill_Dash_Ready", 0, 0f);
|
|
|
|
|
|
yield return new WaitForSeconds(0.5f);
|
|
|
|
|
|
|
|
|
|
|
|
// 돌진
|
2026-02-10 15:29:22 +00:00
|
|
|
|
if (rb != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
rb.isKinematic = false;
|
|
|
|
|
|
rb.velocity = transform.forward * 20f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (animator != null) animator.Play("Skill_Dash_Go", 0, 0f);
|
2026-02-10 15:29:22 +00:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// NavMeshAgent 복구
|
|
|
|
|
|
if (agent != null && isBattleStarted)
|
|
|
|
|
|
{
|
|
|
|
|
|
agent.enabled = true;
|
|
|
|
|
|
// NavMesh 복귀 대기
|
|
|
|
|
|
int retry = 0;
|
|
|
|
|
|
while (agent != null && !agent.isOnNavMesh && retry < 10)
|
|
|
|
|
|
{
|
|
|
|
|
|
retry++;
|
|
|
|
|
|
yield return new WaitForSeconds(0.1f);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
OnAttackEnd();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 2. 스매시
|
2026-02-10 15:29:22 +00:00
|
|
|
|
private IEnumerator Pattern_SmashAttack()
|
|
|
|
|
|
{
|
|
|
|
|
|
OnAttackStart();
|
2026-02-11 13:47:53 +00:00
|
|
|
|
|
|
|
|
|
|
if (animator != null) animator.Play("Skill_Smash_Impact", 0, 0f);
|
2026-02-10 15:29:22 +00:00
|
|
|
|
yield return new WaitForSeconds(1.2f);
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (animator != null) animator.Play("Skill_Smash_Impact", 0, 0f);
|
2026-02-10 15:29:22 +00:00
|
|
|
|
yield return new WaitForSeconds(1.0f);
|
2026-02-11 13:47:53 +00:00
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
OnAttackEnd();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 3. 쓸기
|
2026-02-10 15:29:22 +00:00
|
|
|
|
private IEnumerator Pattern_ShieldWall()
|
|
|
|
|
|
{
|
|
|
|
|
|
OnAttackStart();
|
2026-02-11 13:47:53 +00:00
|
|
|
|
|
|
|
|
|
|
if (animator != null) animator.Play("Skill_Sweep", 0, 0f);
|
2026-02-10 15:29:22 +00:00
|
|
|
|
yield return new WaitForSeconds(2.0f);
|
2026-02-11 13:47:53 +00:00
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
OnAttackEnd();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
// 4. 공 던지기
|
2026-02-11 07:21:58 +00:00
|
|
|
|
private IEnumerator Pattern_ThrowBall()
|
2026-02-10 15:29:22 +00:00
|
|
|
|
{
|
|
|
|
|
|
OnAttackStart();
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (animator != null) animator.Play("Attack_Throw", 0, 0f);
|
|
|
|
|
|
yield return new WaitForSeconds(0.5f);
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
if (ironBall != null && ballRb != null)
|
2026-02-11 07:21:58 +00:00
|
|
|
|
{
|
|
|
|
|
|
ironBall.transform.SetParent(null);
|
2026-02-11 13:47:53 +00:00
|
|
|
|
ballRb.isKinematic = false;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
|
2026-02-11 13:47:53 +00:00
|
|
|
|
Vector3 dir = (target.position - transform.position).normalized;
|
|
|
|
|
|
ballRb.AddForce(dir * 20f + Vector3.up * 5f, ForceMode.Impulse);
|
|
|
|
|
|
ballRb.angularDrag = 5f;
|
2026-02-11 07:21:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isWeaponless = true;
|
|
|
|
|
|
|
2026-02-10 15:29:22 +00:00
|
|
|
|
yield return new WaitForSeconds(1.0f);
|
|
|
|
|
|
OnAttackEnd();
|
|
|
|
|
|
}
|
2026-02-11 13:47:53 +00:00
|
|
|
|
|
|
|
|
|
|
// ════════════════════════════════════════
|
|
|
|
|
|
// ⭐ [신규] 피격 시 부모 클래스 StopAllCoroutines 대응
|
|
|
|
|
|
// ════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 부모의 StartHit()이 StopAllCoroutines()를 호출하므로,
|
|
|
|
|
|
/// 보스 전용 상태를 여기서 복구합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected override void OnStartHit()
|
|
|
|
|
|
{
|
|
|
|
|
|
isPerformingAction = false; // 연출 중이었으면 해제
|
|
|
|
|
|
|
|
|
|
|
|
// 공격 중이었으면 해제
|
|
|
|
|
|
if (isAttacking)
|
|
|
|
|
|
{
|
|
|
|
|
|
isAttacking = false;
|
|
|
|
|
|
if (myWeapon != null) myWeapon.DisableHitBox();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 돌진 중 물리 복구
|
|
|
|
|
|
if (rb != null && !rb.isKinematic)
|
|
|
|
|
|
{
|
|
|
|
|
|
rb.velocity = Vector3.zero;
|
|
|
|
|
|
rb.isKinematic = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 피격 종료 후 NavMeshAgent 복구
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override void OnHitEnd()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnHitEnd();
|
|
|
|
|
|
|
|
|
|
|
|
// 전투 중이면 Agent 다시 활성화
|
|
|
|
|
|
if (isBattleStarted && agent != null && !agent.enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
agent.enabled = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ════════════════════════════════════════
|
|
|
|
|
|
// ⭐ [신규] 사망 처리
|
|
|
|
|
|
// ════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnDie()
|
|
|
|
|
|
{
|
|
|
|
|
|
isBattleStarted = false;
|
|
|
|
|
|
isPerformingAction = false;
|
|
|
|
|
|
isWeaponless = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 물리 정리
|
|
|
|
|
|
if (rb != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
rb.velocity = Vector3.zero;
|
|
|
|
|
|
rb.isKinematic = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 체력바 숨기기
|
|
|
|
|
|
if (bossHealthBar != null) bossHealthBar.SetActive(false);
|
|
|
|
|
|
}
|
2026-02-10 15:29:22 +00:00
|
|
|
|
}
|