| using UnityEngine; |
| using UnityEngine.AI; |
|
|
| namespace UnityAgent |
| { |
| |
| |
| |
| |
| [RequireComponent(typeof(NavMeshAgent))] |
| public class NPCController : MonoBehaviour |
| { |
| public enum State { Idle, Wander, Chase } |
|
|
| [Header("References")] |
| public Transform player; |
|
|
| [Header("Behaviour")] |
| public State initialState = State.Wander; |
| public float wanderRadius = 20f; |
| public float aggroRange = 12f; |
| public float loseSightRange = 20f; |
| public float idleDuration = 2f; |
| public float wanderSpeed = 2.5f; |
| public float chaseSpeed = 5f; |
|
|
| [Header("Debug")] |
| public bool drawGizmos = true; |
|
|
| private NavMeshAgent _agent; |
| private State _state; |
| private float _stateTimer; |
| private Vector3 _startPos; |
|
|
| private void Awake() |
| { |
| _agent = GetComponent<NavMeshAgent>(); |
| _agent.speed = wanderSpeed; |
| } |
|
|
| private void Start() |
| { |
| _state = initialState; |
| _startPos = transform.position; |
| if (player == null && GameObject.FindGameObjectWithTag("Player") != null) |
| player = GameObject.FindGameObjectWithTag("Player").transform; |
| } |
|
|
| private void Update() |
| { |
| switch (_state) |
| { |
| case State.Idle: TickIdle(); break; |
| case State.Wander: TickWander(); break; |
| case State.Chase: TickChase(); break; |
| } |
| } |
|
|
| private void TickIdle() |
| { |
| _stateTimer -= Time.deltaTime; |
| if (PlayerInRange(aggroRange)) { EnterChase(); return; } |
| if (_stateTimer <= 0f) EnterWander(); |
| } |
|
|
| private void TickWander() |
| { |
| if (PlayerInRange(aggroRange)) { EnterChase(); return; } |
| if (_agent.remainingDistance <= _agent.stoppingDistance && !_agent.pathPending) |
| EnterIdle(); |
| } |
|
|
| private void TickChase() |
| { |
| if (player == null) { EnterWander(); return; } |
| _agent.SetDestination(player.position); |
|
|
| if (!PlayerInRange(loseSightRange)) EnterWander(); |
| } |
|
|
| private void EnterIdle() |
| { |
| _state = State.Idle; |
| _stateTimer = idleDuration; |
| _agent.isStopped = true; |
| } |
|
|
| private void EnterWander() |
| { |
| _state = State.Wander; |
| _agent.speed = wanderSpeed; |
| _agent.isStopped = false; |
| Vector3 dir = Random.insideUnitSphere * wanderRadius; |
| dir.y = 0f; |
| Vector3 target = _startPos + dir; |
| if (NavMesh.SamplePosition(target, out NavMeshHit hit, wanderRadius, NavMesh.AllAreas)) |
| _agent.SetDestination(hit.position); |
| } |
|
|
| private void EnterChase() |
| { |
| _state = State.Chase; |
| _agent.speed = chaseSpeed; |
| _agent.isStopped = false; |
| } |
|
|
| private bool PlayerInRange(float range) |
| { |
| if (player == null) return false; |
| return Vector3.Distance(transform.position, player.position) <= range; |
| } |
|
|
| private void OnDrawGizmosSelected() |
| { |
| if (!drawGizmos) return; |
| Gizmos.color = Color.yellow; |
| Gizmos.DrawWireSphere(transform.position, aggroRange); |
| Gizmos.color = Color.red; |
| Gizmos.DrawWireSphere(transform.position, loseSightRange); |
| Gizmos.color = Color.green; |
| Gizmos.DrawWireSphere(_startPos == Vector3.zero ? transform.position : _startPos, wanderRadius); |
| } |
| } |
| } |
|
|