using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; using System; public class NormalMonster : MonsterClass { [Header("전투")] [SerializeField] private float damage = 10f; [SerializeField] private float attackRange = 2f; [SerializeField] private float attackDelay = 1.5f; [SerializeField] private float postAttackDelay = 1.5f; private float lastAttackTime; // ⭐ MonsterClass에 OnHealthChanged가 있으므로 여기서는 삭제합니다. [Header("공격 애니메이션")] [SerializeField] private string[] attackAnimations = { "Monster_Attack_1" }; [Header("이동 애니메이션")] [SerializeField] private string Monster_Idle = "Monster_Idle"; [SerializeField] private string Monster_Walk = "Monster_Walk"; [Header("AI")] [SerializeField] private float stopBuffer = 0.3f; [SerializeField] private float patrolRadius = 5f; [SerializeField] private float patrolInterval = 2f; private float nextPatrolTime; private float repathInterval = 0.3f; private float nextRepathTime; private Transform player; private int attackIndex; private bool isAttacking; private bool isPlayerInZone; protected override void Start() { base.Start(); player = GameObject.FindWithTag("Player")?.transform; agent.stoppingDistance = attackRange - 0.4f; animator.applyRootMotion = false; } void Update() { if (isDead || player == null) return; // ⭐ CS0103 에러 해결: 아래 정의된 Patrol()을 호출 if (isPlayerInZone) HandlePlayerTarget(); else Patrol(); // ⭐ CS0103 에러 해결: 아래 정의된 UpdateMovementAnimation()을 호출 UpdateMovementAnimation(); } void HandlePlayerTarget() { float distance = Vector3.Distance(transform.position, player.position); if (distance <= agent.stoppingDistance + 0.05f) { agent.isStopped = true; agent.velocity = Vector3.zero; } else if (!isAttacking) { agent.isStopped = false; } if (distance <= attackRange - stopBuffer) { if (!isAttacking) TryAttack(); } else if (Time.time >= nextRepathTime && !isAttacking) { agent.SetDestination(player.position); nextRepathTime = Time.time + repathInterval; } } void TryAttack() { if (Time.time < lastAttackTime + attackDelay) return; lastAttackTime = Time.time; string attackName = attackAnimations[attackIndex]; attackIndex = (attackIndex + 1) % attackAnimations.Length; isAttacking = true; agent.isStopped = true; agent.velocity = Vector3.zero; animator.Play(attackName, 0, 0f); } // ⭐ CS0506 해결: 부모의 virtual 메서드를 정상적으로 재정의 public override void TakeDamage(float amount) { base.TakeDamage(amount); // 부모의 OnDamaged(UI 신호 포함)를 실행 } public void OnAttackEnd() { StartCoroutine(PostAttackWait()); } private IEnumerator PostAttackWait() { yield return new WaitForSeconds(postAttackDelay); isAttacking = false; if (agent && agent.isOnNavMesh) agent.isStopped = false; } public void OnAttackHit() { if (player == null) return; PlayerHealth playerHealth = player.GetComponent(); if (playerHealth != null) playerHealth.TakeDamage(damage); } // ⭐ 복구된 이동 애니메이션 함수 void UpdateMovementAnimation() { if (isAttacking || isHit) return; if (agent.velocity.magnitude < 0.1f) animator.Play(Monster_Idle); else animator.Play(Monster_Walk); } // ⭐ 복구된 순찰 함수 void Patrol() { if (Time.time < nextPatrolTime) return; Vector3 randomPoint = transform.position + new Vector3(UnityEngine.Random.Range(-patrolRadius, patrolRadius), 0, UnityEngine.Random.Range(-patrolRadius, patrolRadius)); if (NavMesh.SamplePosition(randomPoint, out NavMeshHit hit, 3f, NavMesh.AllAreas)) agent.SetDestination(hit.position); nextPatrolTime = Time.time + patrolInterval; } private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = true; } private void OnTriggerExit(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = false; } protected override void StartHit() { isAttacking = false; if (agent && agent.isOnNavMesh) agent.isStopped = false; base.StartHit(); } }