using UnityEngine; using UnityEngine.AI; using System.Collections; /// /// 자폭 몬스터 (Kamikaze) /// - 플레이어에게 전력 질주 /// - 범위 내 진입 시 제자리에서 카운트다운 후 폭발 /// public class ExplodeMonster : MonsterClass { [Header("=== 자폭 설정 ===")] [SerializeField] private float explodeRange = 4f; // 데미지 입히는 범위 (폭발 반경) [SerializeField] private float triggerRange = 2.5f; // 폭발을 시작하는 거리 (이 안에 들어오면 카운트다운) [SerializeField] private float fuseTime = 1.5f; // 지연 시간 (도망갈 기회 줌) [SerializeField] private float explosionDamage = 50f; // 폭발 데미지 [Header("폭발 효과")] [SerializeField] private GameObject explosionEffectPrefab; // 쾅! 이펙트 프리팹 [SerializeField] private ParticleSystem fuseEffect; // 몸에서 불꽃 튀는 이펙트 (선택) [SerializeField] private AudioClip fuseSound; // 치익~ 소리 [SerializeField] private AudioClip explosionSound; // 쾅! 소리 [Header("애니메이션")] [SerializeField] private string runAnimation = "Monster_Run"; // 달리기 [SerializeField] private string fuseAnimation = "Monster_Fuse"; // 부들부들(폭발 준비) [Header("AI 설정")] [SerializeField] private float chaseSpeed = 6f; // 이동 속도 (다른 애들보다 빨라야 무서움) [SerializeField] private float patrolRadius = 5f; [SerializeField] private float patrolInterval = 2f; private bool isExploding = false; // 폭발 진행 중? private bool hasExploded = false; // 이미 터졌나? private float nextPatrolTime; private bool isPlayerInZone; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 초기화 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ protected override void Init() { if (agent != null) { agent.speed = chaseSpeed; // 속도 설정 agent.stoppingDistance = 0.5f; // 바짝 붙음 } if (animator != null) animator.applyRootMotion = false; if (fuseEffect != null) fuseEffect.Stop(); } protected override void OnResetStats() { isExploding = false; hasExploded = false; if (fuseEffect != null) fuseEffect.Stop(); } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // AI 로직 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ protected override void ExecuteAILogic() { if (isHit || isExploding || hasExploded) return; float distance = Vector3.Distance(transform.position, playerTransform.position); // 플레이어가 감지 범위(15m) 안에 있거나 트리거에 닿았으면 추격 if (isPlayerInZone || distance <= 15f) { ChasePlayer(distance); } else { Patrol(); UpdateMovementAnimation(); } } void ChasePlayer(float distance) { // 1. 폭발 시작 거리 안으로 들어왔다? -> 점화! if (distance <= triggerRange) { StartCoroutine(ExplodeRoutine()); return; } // 2. 아직 멀었다? -> 전력 질주 if (agent.isOnNavMesh) { agent.isStopped = false; agent.SetDestination(playerTransform.position); // 달리기 애니메이션 animator.Play(runAnimation); } } void UpdateMovementAnimation() { if (isExploding || isHit) return; if (agent.velocity.magnitude > 0.1f) animator.Play(runAnimation); else animator.Play(Monster_Idle); } void Patrol() { if (Time.time < nextPatrolTime) return; Vector3 randomPoint = transform.position + new Vector3( Random.Range(-patrolRadius, patrolRadius), 0, Random.Range(-patrolRadius, patrolRadius) ); if (NavMesh.SamplePosition(randomPoint, out NavMeshHit hit, 3f, NavMesh.AllAreas)) if (agent.isOnNavMesh) agent.SetDestination(hit.position); nextPatrolTime = Time.time + patrolInterval; } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 💣 폭발 시퀀스 (점화 -> 대기 -> 쾅!) // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ IEnumerator ExplodeRoutine() { if (hasExploded) yield break; isExploding = true; hasExploded = true; isAttacking = true; // 부모 클래스 간섭 방지 // ⭐ [핵심] 급브레이크! (문워크 방지) if (agent.isOnNavMesh) { agent.isStopped = true; agent.ResetPath(); // 목적지 삭제 agent.velocity = Vector3.zero; // 속도 0 } Debug.Log($"💣 자폭 카운트다운! ({fuseTime}초 뒤 폭발)"); // 1. 부들부들 떨기 (폭발 준비 모션) if (!string.IsNullOrEmpty(fuseAnimation)) animator.Play(fuseAnimation); // 2. 치익~ 소리 & 이펙트 if (fuseEffect != null) fuseEffect.Play(); if (fuseSound != null && audioSource != null) audioSource.PlayOneShot(fuseSound); // 3. 도망갈 시간 주기 yield return new WaitForSeconds(fuseTime); // 4. 쾅! PerformExplosion(); } void PerformExplosion() { Debug.Log("💥💥💥 쾅!!!"); // 폭발 이펙트 생성 (Particle System) if (explosionEffectPrefab != null) { Instantiate(explosionEffectPrefab, transform.position, Quaternion.identity); } // 폭발음 if (explosionSound != null) { AudioSource.PlayClipAtPoint(explosionSound, transform.position, 1f); } // 주변 데미지 처리 DamageNearbyTargets(); // 몬스터 사망 처리 (MonsterClass 기능 사용) Die(); } void DamageNearbyTargets() { // 폭발 범위(Sphere) 안에 있는 모든 물체 검사 Collider[] hitColliders = Physics.OverlapSphere(transform.position, explodeRange); foreach (Collider hit in hitColliders) { // 플레이어 데미지 if (hit.CompareTag("Player")) { if (hit.TryGetComponent(out var playerHealth)) { if (!playerHealth.isInvincible) { playerHealth.TakeDamage(explosionDamage); Debug.Log($"💥 플레이어 폭사! 데미지: {explosionDamage}"); } } } // (선택사항) 주변 몬스터도 팀킬? else if (hit.CompareTag("Enemy") && hit.gameObject != gameObject) { // 팀킬 로직이 필요하면 여기에 추가 } } } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 유틸리티 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 자폭병은 때려도 폭발 안 멈춤 (취향에 따라 수정 가능) protected override void OnStartHit() { } // 죽을 때 퓨즈 끄기 protected override void OnDie() { if (fuseEffect != null && fuseEffect.isPlaying) fuseEffect.Stop(); } private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = true; } private void OnTriggerExit(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = false; } // 에디터에서 범위 보여주기 private void OnDrawGizmosSelected() { Gizmos.color = Color.red; Gizmos.DrawWireSphere(transform.position, triggerRange); // 감지 범위 Gizmos.color = new Color(1f, 0.5f, 0f, 0.3f); Gizmos.DrawSphere(transform.position, explodeRange); // 폭발 데미지 범위 } }