using UnityEngine; using UnityEngine.AI; using System.Collections; /// /// 자폭 몬스터 /// - 플레이어에게 빠르게 접근 /// - 일정 거리 내에 들어오면 폭발 /// - 범위 내 모든 대상에게 데미지 /// - 자신도 사망 /// public class ExplodeMonster : MonsterClass { [Header("=== 자폭 설정 ===")] [SerializeField] private float explodeRange = 3f; // 폭발 범위 [SerializeField] private float triggerRange = 2f; // 폭발 시작 거리 [SerializeField] private float fuseTime = 1.5f; // 폭발까지 지연 시간 [SerializeField] private float explosionDamage = 50f; // 폭발 데미지 (기본 공격력 무시) [SerializeField] private bool damagesSelf = true; // 자신도 죽는가? [Header("폭발 효과")] [SerializeField] private GameObject explosionEffectPrefab; // 폭발 이펙트 [SerializeField] private ParticleSystem fuseEffect; // 퓨즈 불꽃 효과 [SerializeField] private AudioClip fuseSound; // 치익~ 소리 [SerializeField] private AudioClip explosionSound; // 폭발음 [SerializeField] private float cameraShakeIntensity = 0.5f; // 화면 흔들림 [Header("애니메이션")] [SerializeField] private string runAnimation = "Monster_Run"; // 달리기 [SerializeField] private string fuseAnimation = "Monster_Fuse"; // 폭발 준비 [SerializeField] private string explodeAnimation = "Monster_Explode"; // 폭발 [Header("AI 설정")] [SerializeField] private float chaseSpeed = 5f; // 추격 속도 (빠르게!) [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); // 플레이어 발견하면 무조건 돌진! if (isPlayerInZone || distance <= 15f) { ChasePlayer(distance); } else { Patrol(); } UpdateMovementAnimation(); } void ChasePlayer(float distance) { // 폭발 범위 안에 들어오면 폭발! if (distance <= triggerRange) { StartCoroutine(Explode()); return; } // 플레이어에게 전력 질주! if (agent.isOnNavMesh) { agent.SetDestination(playerTransform.position); } } 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 Explode() { if (hasExploded) yield break; isExploding = true; hasExploded = true; // 이동 멈춤 if (agent.isOnNavMesh) { agent.isStopped = true; agent.velocity = Vector3.zero; } Debug.Log($"[ExplodeMonster] 💣 폭발 카운트다운 시작! ({fuseTime}초)"); // 폭발 준비 애니메이션 if (!string.IsNullOrEmpty(fuseAnimation)) { animator.Play(fuseAnimation, 0, 0f); } // 퓨즈 이펙트 시작 if (fuseEffect != null) { fuseEffect.Play(); } // 치익~ 소리 if (fuseSound != null && audioSource != null) { audioSource.PlayOneShot(fuseSound); } // 대기 (퓨즈 타는 시간) yield return new WaitForSeconds(fuseTime); // 💥 폭발! PerformExplosion(); } void PerformExplosion() { Debug.Log("[ExplodeMonster] 💥💥💥 폭발!!!"); // 폭발 애니메이션 if (!string.IsNullOrEmpty(explodeAnimation)) { animator.Play(explodeAnimation, 0, 0f); } // 폭발 이펙트 생성 if (explosionEffectPrefab != null) { Instantiate(explosionEffectPrefab, transform.position, Quaternion.identity); } // 폭발음 if (explosionSound != null) { AudioSource.PlayClipAtPoint(explosionSound, transform.position, 1f); } //// 화면 흔들림 (CameraShake가 있다면) //CameraShake cameraShake = Camera.main?.GetComponent(); //if (cameraShake != null) //{ // cameraShake.Shake(cameraShakeIntensity, 0.5f); //} // 범위 내 모든 대상에게 데미지 DamageNearbyTargets(); // 자신도 사망 if (damagesSelf) { Die(); } } void DamageNearbyTargets() { // 폭발 범위 내 모든 Collider 찾기 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($"[ExplodeMonster] 💥 플레이어 적중! 데미지: {explosionDamage}"); } } } // 다른 몬스터도 데미지 받게 하려면 (선택사항) else if (hit.CompareTag("Enemy") && hit.gameObject != gameObject) { if (hit.TryGetComponent(out var damageable)) { damageable.TakeDamage(explosionDamage * 0.5f); // 절반 데미지 Debug.Log($"[ExplodeMonster] 💥 다른 몬스터도 피해! {hit.name}"); } } } } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 피격 시: 즉시 폭발! (선택사항) // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ protected override void OnStartHit() { // 선택사항: 맞으면 바로 폭발하게 하려면 주석 해제 // if (!hasExploded && !isExploding) // { // StartCoroutine(Explode()); // } } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 사망 시: 폭발 효과 정리 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ protected override void OnDie() { if (fuseEffect != null && fuseEffect.isPlaying) { fuseEffect.Stop(); } } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Trigger 감지 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = true; } private void OnTriggerExit(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = false; } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Gizmos (폭발 범위 시각화) // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 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); // 외곽선 Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(transform.position, explodeRange); } }