public class CitizenAI : MonoBehaviour { public Transform _myTransform; public Transform workLocation; public Transform target; public float idleTime; public enum State{ Hungry, LookingForWork, Working, BringingRessourcesToWork, Hunting, Idle, Waiting, //Many more states but you get the idea }; private float speed; private float idleTimer; void Update() { //Run any movement code that hasn't ran this frame MoveTorwards(); switch (state){ //There's a case for every possible State, only listing relevant one here //When entering a state, a villager should not have a target //Villager just picked up resources from a storage barn and is bringing them //to his workplace. case State.BringingRessourcesToWork: if(!target) //If there is no destination, acquire one. { target = workLocation; //Make a call to StartPath only once seeker.StartPath(_myTransform.position, target.position, OnPathComplete); currentStateString = "Bringing needed ressources to work."; }//villager has a valid target, only check if within range //Here I check to see if the unit is within range of his destination //I could probably check if the waypoint is path.vectorPath[path.vectorPath.Length - 1] if(SquareDistance (_myTransform.position, target.position) < 4.5f) { dropRessoucesAtWork(); //drop work related resources target = null; //clear the destination reference state = assesNeeds(); //get a new state }//end if distance is short enough break; //Here is another case where I perdiodically need to refresh the path. case State.HuntingAnimal: if(!target){ //Same principle as above, acquire a target and call StartPath target = workLocation.parent.GetComponent().getNextTarget(); if(target){ seeker.StartPath (_myTransform.position, target.position, OnPathComplete); idleTimer = 8.0f; //Here I set a timer at 8 seconds currentStateString = "Hunting"; } else{ state = assessNeeds(); return; } } idleTimer -= Time.deltaTime; //Every frame deduct deltaTime from the timer if(idleTimer <= 0){//Once the timer reaches 0 recall StartPath (the deer might have moved) seeker.StartPath (_myTransform.position, target.position, OnPathComplete); idleTimer = 8.0f; } //if we're within a certain distance of the deer, do stuff if(SquareDistance (_myTransform.position, target.position) < 500){ path = null; target.GetComponent().MarkForDeath(); //The code below simply sets the AI not to do anything for 3 seconds and play an archery animation idleTimer = 3.0f; state = State.Waiting; playArcheryAnim(); lastState = State.GoingForKill; idleTimer = idleTime; } break; }//end switch (state) }//end update private void MoveTorwards(){ if (path == null) { return; } if (currentWaypoint >= path.vectorPath.Count) { //End of path reached path = null; return; } if(!bentDown){ playWalkCycle(); } _myTransform.position = Vector3.Slerp (_myTransform.position, path.vectorPath[currentWaypoint], speed * Time.deltaTime); //Check if we are close enough to the next waypoint //If we are, proceed to follow the next waypoint if (SquareDistance(_myTransform.position,path.vectorPath[currentWaypoint]) < 4) { currentWaypoint++; if (currentWaypoint < path.vectorPath.Count) { Vector3 lookDirection = path.vectorPath[currentWaypoint]; lookDirection.y = this._myTransform.position.y; transform.LookAt(lookDirection); } return; } } private float SquareDistance(Vector3 a, Vector3 b){ Vector3 diff = b - a; return diff.sqrMagnitude; } }