using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; // Cần thiết để dùng NavMesh [RequireComponent(typeof(NavMeshAgent))] // Tự động thêm component này nếu chưa có public class EnemyAI : MonoBehaviour { [Header("References")] public Transform player; [Header("Detection")] public float detectRange = 10f; public float moveSpeed = 3f; public float rotateSpeed = 50f; [Header("Patrol Area")] public float patrolRadius = 15f; // Bán kính khu vực tuần tra public float patrolWaitTime = 2f; // Thời gian đứng chờ trước khi đi điểm khác private Vector3 startPosition; private float currentWaitTime; [Header("Artifact")] public bool playerHasArtifact; [Header("Laser")] public GameObject laserPrefab; public Transform firePoint; public float minShootDelay = 1f; public float maxShootDelay = 3f; private float nextShootTime; private NavMeshAgent agent; public Node behaviorTreeRoot; private void Start() { agent = GetComponent(); agent.speed = moveSpeed; // Lưu lại vị trí ban đầu để làm tâm của khu vực tuần tra startPosition = transform.position; nextShootTime = Time.time + Random.Range(minShootDelay, maxShootDelay); InitBehaviorTree(); FindPlayer(); } private void Update() { // Nếu mất reference (Player chết hoặc chưa spawn), liên tục tìm lại if (player == null) { FindPlayer(); } // Chỉ chạy AI nếu đã tìm thấy player (hoặc bạn có thể cho tuần tra ngay cả khi chưa có player tùy logic game) behaviorTreeRoot?.Evaluate(); } private void FindPlayer() { GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); if (playerObj != null) { player = playerObj.transform; } } private void InitBehaviorTree() { // Player có artifact -> focus + shoot var laserSequence = new Sequence(new List { new TaskNode(CheckHasArtifact), new TaskNode(ActionFocusAndShoot) }); // Thấy player -> chạy tới var chaseSequence = new Sequence(new List { new TaskNode(CheckCanSeePlayer), new TaskNode(ActionMoveToPlayer) }); // Không thấy ai -> Tuần tra bằng NavMesh var patrolNode = new TaskNode(ActionPatrol); behaviorTreeRoot = new Selector(new List { laserSequence, chaseSequence, patrolNode }); } #region CONDITIONS private NodeState CheckHasArtifact() { return playerHasArtifact ? NodeState.Success : NodeState.Failure; } private NodeState CheckCanSeePlayer() { if (player == null) return NodeState.Failure; float distance = Vector3.Distance(transform.position, player.position); if (distance <= detectRange) { return NodeState.Success; } return NodeState.Failure; } #endregion #region ACTIONS private NodeState ActionPatrol() { // Debug.Log("Patrolling..."); if (!agent.isActiveAndEnabled || !agent.isOnNavMesh) return NodeState.Failure; agent.isStopped = false; // Đảm bảo NPC được phép di chuyển agent.speed = moveSpeed * 0.5f; // Đi dạo nên đi chậm lại một chút // Kiểm tra xem NPC đã đến điểm đích chưa if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance) { currentWaitTime += Time.deltaTime; // Chờ một lúc rồi mới chọn điểm mới if (currentWaitTime >= patrolWaitTime) { // Tìm một điểm ngẫu nhiên trong bán kính cho trước Vector3 randomDirection = Random.insideUnitSphere * patrolRadius; randomDirection += startPosition; NavMeshHit hit; // Đảm bảo điểm ngẫu nhiên nằm trên bề mặt NavMesh hợp lệ if (NavMesh.SamplePosition(randomDirection, out hit, patrolRadius, 1)) { agent.SetDestination(hit.position); } currentWaitTime = 0f; } } return NodeState.Running; } private NodeState ActionMoveToPlayer() { if (player == null) return NodeState.Failure; // Debug.Log("Chasing Player"); if (!agent.isActiveAndEnabled || !agent.isOnNavMesh) return NodeState.Failure; agent.isStopped = false; agent.speed = moveSpeed; // Phục hồi tốc độ rượt đuổi agent.SetDestination(player.position); return NodeState.Running; } private NodeState ActionFocusAndShoot() { if (player == null) return NodeState.Failure; // Debug.Log("Focus and Shoot!"); if (!agent.isActiveAndEnabled || !agent.isOnNavMesh) return NodeState.Failure; // Dừng NavMeshAgent lại để đứng bắn, tránh bị trượt agent.isStopped = true; // Focus player Vector3 dir = player.position - transform.position; dir.y = 0f; if (dir != Vector3.zero) { Quaternion targetRotation = Quaternion.LookRotation(dir); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotateSpeed * Time.deltaTime); } // Shoot with random delay if (Time.time >= nextShootTime) { ShootLaser(); nextShootTime = Time.time + Random.Range(minShootDelay, maxShootDelay); } return NodeState.Running; } private void ShootLaser() { if (laserPrefab == null || firePoint == null) return; Instantiate(laserPrefab, firePoint.position, firePoint.rotation); // Debug.Log("Laser Shot!"); } #endregion }