using UnityEngine; using UnityEngine.AI; using System.Collections; public class SmartThrowMonster : MonsterClass { public enum ThrowType { Unlimited, Reload } // 무제한(활) vs 재장전(돌) [Header("=== 투척/원거리 설정 ===")] [SerializeField] private ThrowType attackType = ThrowType.Unlimited; [SerializeField] private GameObject projectilePrefab; // 날아갈 물체 [SerializeField] private Transform firePoint; // 발사 위치 [SerializeField] private GameObject handModel; // 손에 들고 있는 모델 (던지면 꺼짐) [Header("수치 설정")] [SerializeField] private float reloadTime = 2.0f; // 돌 다시 생기는 시간 [SerializeField] private float throwForce = 15f; // 던지는 힘 (곡사) [SerializeField] private float throwUpward = 5f; // 위로 띄우는 힘 [SerializeField] private float attackRange = 10f; [SerializeField] private float attackDelay = 2f; [SerializeField] private float detectRange = 15f; // 플레이어 감지 거리 [Header("애니메이션")] [SerializeField] private string throwAnim = "Monster_Throw"; [SerializeField] private string walkAnim = "Monster_Walk"; // ⭐ [추가] 순찰 관련 변수 [Header("AI 순찰 설정")] [SerializeField] private float patrolRadius = 5f; [SerializeField] private float patrolInterval = 3f; private float lastAttackTime; private bool isReloading = false; // 돌 줍는 중인가? private float nextPatrolTime; // 다음 순찰 시간 private bool isPlayerInZone; // 플레이어가 감지 범위 내에 있는가? protected override void Init() { if (agent != null) agent.stoppingDistance = attackRange * 0.8f; if (animator != null) animator.applyRootMotion = false; } protected override void ExecuteAILogic() { if (isHit || isAttacking || isReloading || isResting) return; float dist = Vector3.Distance(transform.position, playerTransform.position); // 1. 플레이어를 감지했거나(Trigger), 사거리 안에 있거나, 감지 거리 안에 있을 때 -> 전투 모드 if (isPlayerInZone || dist <= attackRange || dist <= detectRange) { if (dist <= attackRange) { TryAttack(); } else { // 사거리 밖이면 추적 if (agent.isOnNavMesh) { agent.isStopped = false; agent.SetDestination(playerTransform.position); UpdateMovementAnimation(); } } } // 2. 플레이어 없음 -> 순찰 모드 else { Patrol(); UpdateMovementAnimation(); } } void TryAttack() { if (Time.time < lastAttackTime + attackDelay) return; if (isReloading) return; // 돌 없으면 못 던짐 lastAttackTime = Time.time; isAttacking = true; // 조준 (멈추고 바라보기) if (agent.isOnNavMesh) { agent.isStopped = true; agent.velocity = Vector3.zero; } transform.LookAt(new Vector3(playerTransform.position.x, transform.position.y, playerTransform.position.z)); animator.Play(throwAnim); } // ⭐ [추가] 순찰 로직 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); agent.isStopped = false; } } nextPatrolTime = Time.time + patrolInterval; } // ⭐ [추가] 움직임 애니메이션 관리 void UpdateMovementAnimation() { if (isAttacking || isHit || isResting) return; if (agent.velocity.magnitude > 0.1f) animator.Play(walkAnim); else animator.Play(Monster_Idle); } // ⭐ 애니메이션 이벤트: OnAttackStart public override void OnAttackStart() { if (projectilePrefab && firePoint) { // 1. 발사체 생성 GameObject obj = Instantiate(projectilePrefab, firePoint.position, transform.rotation); // 데미지 전달 if (obj.TryGetComponent(out var proj)) { // 돌 던지기(Reload) 타입이면 초기 속도 0 (물리력으로 날림) // 활 쏘기(Unlimited) 타입이면 Projectile 자체 속도 사용 float initSpeed = (attackType == ThrowType.Reload) ? 0f : 20f; proj.Initialize(transform.forward, initSpeed, attackDamage); } // 물리력으로 던지기 (돌 던지기 타입일 때만) if (attackType == ThrowType.Reload) { Rigidbody rb = obj.GetComponent(); if (rb) { rb.useGravity = true; Vector3 force = transform.forward * throwForce + Vector3.up * throwUpward; rb.AddForce(force, ForceMode.Impulse); } } // 2. 손에 있는 모델 숨기기 if (attackType == ThrowType.Reload && handModel != null) { handModel.SetActive(false); StartCoroutine(ReloadRoutine()); } } } IEnumerator ReloadRoutine() { isReloading = true; // Debug.Log("🔄 [Mob] 돌 줍는 중..."); // 로그 너무 많이 뜨면 주석 처리 yield return new WaitForSeconds(reloadTime); if (handModel != null) handModel.SetActive(true); isReloading = false; // Debug.Log("✅ [Mob] 장전 완료!"); } // ⭐ [추가] 감지 영역 설정 private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = true; } private void OnTriggerExit(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = false; } }