using UnityEngine; using UnityEngine.AI; using System.Collections; // ⭐ IEnumerator 사용을 위해 추가 using System; public class MonsterClass : MonoBehaviour, IDamageable { [Header("스탯")] [SerializeField] protected float maxHP = 100f; protected float currentHP; public event Action OnHealthChanged; [Header("피격 / 사망 애니메이션")] [SerializeField] protected string Monster_GetDamage = "Monster_GetDamage"; [SerializeField] protected string Monster_Die = "Monster_Die"; protected Animator animator; protected NavMeshAgent agent; protected AudioSource audioSource; protected bool isHit; protected bool isDead; protected bool isAttacking; [Header("AI 설정 (공격 후 휴식)")] [SerializeField] protected float attackRestDuration = 1.5f; // ⭐ 공격 후 쉬는 시간 (초) protected bool isResting; // ⭐ 현재 쉬고 있는 상태인지 확인 [Header("경험치")] [SerializeField] protected int expReward = 10; public static System.Action OnMonsterKilled; [Header("공통 사운드/이펙트")] [SerializeField] protected AudioClip hitSound; [SerializeField] protected AudioClip deathSound; [SerializeField] protected GameObject deathEffectPrefab; [SerializeField] protected ParticleSystem hitEffect; [SerializeField] protected Transform impactSpawnPoint; [Header("--- 드랍 설정 ---")] [SerializeField] private GameObject potionPrefab; [SerializeField][Range(0f, 100f)] private float potionDropChance = 30f; [Space(10)] [SerializeField] private GameObject[] weaponPrefabs; [SerializeField][Range(0f, 100f)] private float weaponDropChance = 5f; protected virtual void Start() { currentHP = maxHP; animator = GetComponent(); agent = GetComponent(); audioSource = GetComponent(); OnHealthChanged?.Invoke(currentHP, maxHP); } protected virtual void Update() { if (isDead) return; // ⭐ [수정] 피격(isHit), 공격(isAttacking), 또는 휴식(isResting) 중이면 이동을 멈춥니다. bool shouldStop = isHit || isAttacking || isResting; if (agent != null && agent.isOnNavMesh) { agent.isStopped = shouldStop; if (shouldStop) agent.velocity = Vector3.zero; } if (animator != null) { // 멈췄을 때는 속도를 0으로 전달하여 Idle 애니메이션이 나오게 합니다. float currentSpeed = (agent != null && !shouldStop) ? agent.velocity.magnitude : 0f; animator.SetFloat("Speed", currentSpeed); } } public virtual void TakeDamage(float amount) { OnDamaged(amount); } public virtual void OnDamaged(float damage) { if (isDead) return; currentHP -= damage; OnHealthChanged?.Invoke(currentHP, maxHP); if (currentHP <= 0) { Die(); return; } if (!isHit) StartHit(); } protected virtual void StartHit() { isHit = true; isAttacking = false; isResting = false; // ⭐ 맞으면 쉬는 걸 중단하고 바로 아파해야 합니다. StopAllCoroutines(); // 진행 중인 휴식 루틴을 끕니다. if (agent && agent.isOnNavMesh) { agent.isStopped = true; agent.velocity = Vector3.zero; } animator.Play(Monster_GetDamage, 0, 0f); if (hitEffect) hitEffect.Play(); if (hitSound) audioSource.PlayOneShot(hitSound); } public virtual void OnAttackStart() { isAttacking = true; isResting = false; } // ⭐ [수정] 공격이 끝나면 바로 움직이는 게 아니라 '휴식' 코루틴을 실행합니다. public virtual void OnAttackEnd() { isAttacking = false; if (!isDead && !isHit) { StartCoroutine(RestAfterAttack()); } } // ⭐ 공격 후 대기 코루틴 private IEnumerator RestAfterAttack() { isResting = true; yield return new WaitForSeconds(attackRestDuration); // 설정한 시간만큼 대기 isResting = false; } public virtual void OnHitEnd() { isHit = false; if (agent && agent.isOnNavMesh) agent.isStopped = false; } protected virtual void Die() { if (isDead) return; isDead = true; Collider col = GetComponent(); if (col != null) col.enabled = false; OnMonsterKilled?.Invoke(expReward); TryDropItems(); if (agent && agent.isOnNavMesh) { agent.isStopped = true; agent.velocity = Vector3.zero; } animator.Play(Monster_Die, 0, 0f); if (deathSound) audioSource.PlayOneShot(deathSound); Destroy(gameObject, 1.5f); } private void TryDropItems() { if (potionPrefab != null && UnityEngine.Random.Range(0f, 100f) <= potionDropChance) SpawnItem(potionPrefab); if (weaponPrefabs != null && weaponPrefabs.Length > 0 && UnityEngine.Random.Range(0f, 100f) <= weaponDropChance) { int randomIndex = UnityEngine.Random.Range(0, weaponPrefabs.Length); SpawnItem(weaponPrefabs[randomIndex]); } } private void SpawnItem(GameObject prefab) { Vector3 spawnPos = transform.position + Vector3.up * 0.5f; GameObject item = Instantiate(prefab, spawnPos, Quaternion.identity); item.transform.localScale = prefab.transform.localScale; if (item.TryGetComponent(out var rb)) { Vector3 popDir = Vector3.up * 0.02f + UnityEngine.Random.insideUnitSphere * 0.02f; rb.AddForce(popDir, ForceMode.Impulse); if (prefab != potionPrefab) rb.AddTorque(UnityEngine.Random.insideUnitSphere * 0.3f, ForceMode.Impulse); } } public void OnDeathAnimEnd() { } }