Projext/Assets/Scripts/Enemy/AI/ExplodeMonster.cs

310 lines
11 KiB
C#
Raw Normal View History

2026-02-04 14:06:25 +00:00
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
/// <summary>
/// 자폭 몬스터
/// - 플레이어에게 빠르게 접근
/// - 일정 거리 내에 들어오면 폭발
/// - 범위 내 모든 대상에게 데미지
/// - 자신도 사망
/// </summary>
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<CameraShake>();
//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<PlayerHealth>(out var playerHealth))
{
if (!playerHealth.isInvincible)
{
playerHealth.TakeDamage(explosionDamage);
Debug.Log($"[ExplodeMonster] 💥 플레이어 적중! 데미지: {explosionDamage}");
}
}
}
// 다른 몬스터도 데미지 받게 하려면 (선택사항)
else if (hit.CompareTag("Enemy") && hit.gameObject != gameObject)
{
if (hit.TryGetComponent<IDamageable>(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);
}
}