using UnityEngine; using UnityEngine.AI; using System.Collections; /// /// 돌진 공격 몬스터 /// - 플레이어를 향해 빠르게 돌진 /// - 부딪히면 데미지 /// public class ChargeMonster : MonsterClass { [Header("=== 돌진 공격 설정 ===")] [SerializeField] private float chargeSpeed = 15f; // 돌진 속도 [SerializeField] private float chargeRange = 10f; // 돌진 시작 거리 [SerializeField] private float chargeDuration = 2f; // 돌진 지속 시간 [SerializeField] private float chargeDelay = 3f; // 돌진 쿨타임 [SerializeField] private float prepareTime = 0.5f; // 돌진 준비 시간 private float lastChargeTime; private bool isCharging = false; private bool isPreparing = false; private Vector3 chargeDirection; [Header("공격 / 이동 애니메이션")] [SerializeField] private string chargeAnimation = "Monster_Charge"; [SerializeField] private string prepareAnimation = "Monster_ChargePrepare"; [SerializeField] private string Monster_Walk = "Monster_Walk"; [Header("AI 상세 설정")] [SerializeField] private float patrolRadius = 5f; [SerializeField] private float patrolInterval = 2f; private float nextPatrolTime; private bool isPlayerInZone; private Rigidbody _rigidbody; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 초기화 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ protected override void Awake() { base.Awake(); _rigidbody = GetComponent(); if (_rigidbody == null) { _rigidbody = gameObject.AddComponent(); } _rigidbody.isKinematic = true; // NavMeshAgent 사용 시 Kinematic } protected override void Init() { if (agent != null) { agent.stoppingDistance = 1f; } if (animator != null) animator.applyRootMotion = false; } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // AI 로직 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ protected override void ExecuteAILogic() { if (isHit || isCharging || isPreparing) return; float distance = Vector3.Distance(transform.position, playerTransform.position); if (isPlayerInZone || distance <= chargeRange * 1.5f) { HandleChargeCombat(distance); } else { Patrol(); } UpdateMovementAnimation(); } void HandleChargeCombat(float distance) { // 돌진 범위 안이면 돌진 준비 if (distance <= chargeRange && Time.time >= lastChargeTime + chargeDelay) { StartCoroutine(PrepareCharge()); } // 아니면 추적 else if (!isCharging) { if (agent.isOnNavMesh) agent.SetDestination(playerTransform.position); } } IEnumerator PrepareCharge() { isPreparing = true; // 이동 멈춤 if (agent.isOnNavMesh) { agent.isStopped = true; agent.velocity = Vector3.zero; } // 플레이어 방향 저장 chargeDirection = (playerTransform.position - transform.position).normalized; chargeDirection.y = 0; // 플레이어 바라보기 transform.rotation = Quaternion.LookRotation(chargeDirection); // 준비 애니메이션 if (!string.IsNullOrEmpty(prepareAnimation)) { animator.Play(prepareAnimation, 0, 0f); } Debug.Log("[ChargeMonster] 돌진 준비 중..."); yield return new WaitForSeconds(prepareTime); // 돌진 시작! StartCoroutine(Charge()); } IEnumerator Charge() { isPreparing = false; isCharging = true; lastChargeTime = Time.time; // NavMeshAgent 끄기 if (agent.isOnNavMesh) agent.enabled = false; // Rigidbody Kinematic 끄기 if (_rigidbody != null) _rigidbody.isKinematic = false; // 돌진 애니메이션 animator.Play(chargeAnimation, 0, 0f); Debug.Log("[ChargeMonster] 돌진 시작!"); float chargeStartTime = Time.time; while (Time.time < chargeStartTime + chargeDuration) { // 빠르게 앞으로 이동 if (_rigidbody != null) { _rigidbody.velocity = chargeDirection * chargeSpeed; } else { transform.position += chargeDirection * chargeSpeed * Time.deltaTime; } yield return null; } // 돌진 종료 if (_rigidbody != null) { _rigidbody.velocity = Vector3.zero; _rigidbody.isKinematic = true; } // NavMeshAgent 다시 켜기 if (agent != null) agent.enabled = true; isCharging = false; Debug.Log("[ChargeMonster] 돌진 종료!"); } void UpdateMovementAnimation() { if (isCharging || isPreparing || isHit || isResting) 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( 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; } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 충돌 감지 (돌진 중 플레이어와 충돌) // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ private void OnCollisionEnter(Collision collision) { if (!isCharging) return; if (collision.gameObject.CompareTag("Player")) { if (collision.gameObject.TryGetComponent(out var playerHealth)) { if (!playerHealth.isInvincible) { playerHealth.TakeDamage(attackDamage); Debug.Log($"[ChargeMonster] 돌진 적중! 데미지: {attackDamage}"); } } } } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Trigger 감지 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = true; } private void OnTriggerExit(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = false; } }