using System.Collections; using System.Collections.Generic; using UnityEngine; // import the unity AI library using UnityEngine.AI; [RequireComponent(typeof(NavMeshAgent))] public class AICharacter : BaseCharacter { // the target for the AI character to chase [SerializeField] private Transform target = null; // the max range at which to start chasing the target [SerializeField] private float targetChaseRange = 10.0f; // the distance within which to stop from the target [SerializeField] private float targetStoppingDistance = 2f; // indicates how long AI should stop and think (be idle) on reaching a destination waypoint (not target) so it is not constantly moving [SerializeField] private float thinkingTime = 5f; // used to cache the nav mesh agent component attached to this character private NavMeshAgent agent; // used to cache the waypoint controller component attached to this character WaypointController waypointController; // indicates if AI is currently in thinking mode private bool isThinking = false; // store the elapsed thinking (idle) time when AI is in thinking mode float elapsedThinkingTime = 0f; // The current target waypoint to visit Transform currentWaypoint; // The agent's current target destination, useful for checking if current destination is player or waypoint Transform currentDestination; //private bool isChasing; //private bool wasAttacking; // Start is called before the first frame update // Make start override Start method in base (inherited) class protected override void Start() { // Call the base (inherited) class start method first base.Start(); // Get and cache the nav mesh agent component attached to the game object this script is attached to agent = GetComponent(); // Get and cache the waypoint controller component attached to the game object this script is attached to waypointController = GetComponent(); // Set a new target waypoint to visit SetNewWaypoint(); // Set the attack range to the target's stopping distance attackRange = targetStoppingDistance; // Set non-combat mode based on setting //SetNonCombat(nonCombatMode); } // Update is called once per frame void Update() { // if no Nav Mesh Agent component attached to game object, no need to continue, return and exit the method early if (agent == null) return; // Check if AI is attacking and if yes, attack // Else // Check if AI is near enough to chase and if yes, update current destination to target // Else if AO is thinking, update the thinking // Else update the current way point // Move the AI passing its movement (agent.velocity). Hint: see what you did in player if (IsAttacking()) { Attack(); } else { if (IsTargetWithinRange()) // Chase { SetCurrentDestination(target, attackRange); } else if (isThinking) // Think { UpdateThinking(); } else // Patrol { UpdateCurrentWaypoint(); } Move(agent.velocity); } } // Check if AI is in attack mode protected override bool IsAttacking() { // if target or non combat mode or current target destination not set, no one to attack // Otherwise, check if current destination is the target and agent is close enough (within attack range) to attack if (target == null || nonCombatMode || agent.destination == null) return false; else { if (currentDestination == target && HasReachedDestination(attackRange)) { return true; } } return false; } // Set the Nav Mash Agent's target destination private void SetCurrentDestination(Transform destination, float stoppingDistance) { // Update the current destination so you can use to check if current destination is target or player currentDestination = destination; // Update the nav mesh agent's destination and stopping distance to the specified passed in parameters agent.destination = destination.position; agent.stoppingDistance = stoppingDistance; // make the AI agent resume movement to the updated destination agent.isStopped = false; } // Checks if target (player) is within chasing distance private bool IsTargetWithinRange() { // if no target has been set, no need to continue, return and exit the method early if (target == null) return false; // calculate the position vector difference / offset Vector3 positionOffset = target.position - transform.position; // As you only need to check if target is nearby, you do not need the actual distance value // use squared magnitude instead of actual distance which uses square root which is computationally slower than squared magnitude // See more: https://docs.unity3d.com/ScriptReference/Vector3-sqrMagnitude.html return positionOffset.sqrMagnitude <= targetChaseRange * targetChaseRange; } // Check if Nav Mesh Agent has reached its current target destination i.e. remaining distance is less than/equal stopping distance private bool HasReachedDestination(float stoppingDistance) { // Check if AI agent is NOT stopped AND the AI agent's remaining distance is less than the passed in stopping distance parameter // If yes, make the AI agent stop movement (set isStopped property) and return true // Else, make the AI agent resume/continue movement to the destination and return false if (!agent.isStopped && agent.remainingDistance <= stoppingDistance) { agent.isStopped = true; return true; } agent.isStopped = false; return false; } // Reset the thinking duration (elapsedThinkingTime) private void StartThinking() { // What do you think needs to happen to the elapsed thinking time and isThinking variables? elapsedThinkingTime = 0f; isThinking = true; } // Update the thinking duration private void UpdateThinking() { // Increase thinking counter by time elapsed since last frame elapsedThinkingTime += Time.deltaTime; // If the elapsed thinking time is greater than or equal the the thinking time, the AI agent can stop thinking // Update isThinking // Set a new target waypoint to visit - what method allows you to get and set a new waypoint or see what you did in Start if (elapsedThinkingTime >= thinkingTime) { isThinking = false; // No longer thinking, get new waypoint to visit SetNewWaypoint(); } } // Update Nav Mesh Agent's destination back to the current waypoint in case AI took a detour to chase the target (player) private void UpdateCurrentWaypoint() { // Update agent's destination to current waypoint in case it changed in the last last frame and set the stopping distance to the waypoint stopping distance (waypointController.StoppingDistance) SetCurrentDestination(currentWaypoint, waypointController.StoppingDistance); // If agent has reached waypoint, start thinking if (HasReachedDestination(waypointController.StoppingDistance)) { StartThinking(); } } // Gets and sets a new waypoint to visit void SetNewWaypoint() { // If there is a waypoint controller script component attached (will be cached in waypointController) // use the waypointController's Get Next Waypoint method to set (assign) a new currentWaypoint // currentWaypoint = ... if (waypointController != null) { currentWaypoint = waypointController.GetNextWaypoint(); } } }