Very (very) Simple NPC movement pattern...

Hello!

I started making a VERY simple NPC movement pattern between waypoints.

The thing is, it works very well. I am able to make the npc walk between them perfectly.

The trouble is when I try to make it wait between them. I read some examples about AI but whatever I do in my code doesn’t seem to work. I’ve reorganised it several times but with no success.

I’ve got three waypoints in the scene in different positions. The problem, properly speaking, is that it waits some seconds when it reaches the first waypoint, it also does when reaching the second one. But the third expected wait doesn’t happen. Ever.

Also , wait time seems to get affected by the variable “velocidad” (speed in english). Whenever I change it, the wait seems to be shorter (and waitForSeconds is getting a hardcoded 5.0f value so this doesn’t make sense to me).

The code is the following : (ignore comments , they’re in spanish, but the code is pretty straightforward). Also comprobarDistancia stands for “checkDistance” in spanish.

public class waypointer : MonoBehaviour {
	
	// Variables privadas
	private Vector3 waypointActual;
	private int wpElegido;
	
	// @ Variables Publicas
	public float velocidad;
	
	// @ Arrays
	private ArrayList listaWaypoints = new ArrayList();
	
	// @ Comportamiento
	private bool walk = true;
	
	void Start () {
		// @ Agregamos las posiciones de los waypoints a la lista
		GameObject[] waypoints = GameObject.FindGameObjectsWithTag ("waypoint");
		foreach (GameObject waypoint in waypoints){
			listaWaypoints.Add(waypoint.transform.position);
		}
		
		// @ Definimos el primer waypoint al cual dirigirse
		waypointActual = (Vector3) listaWaypoints[0];
	}
	
	
	void Update () {
		comprobarDistancia();
		if (walk)
			Walk ();
		else {
			StartCoroutine(Wait());
		}

	}
	
	void Walk(){
		animation.CrossFade ("walk");
		moveNPCTowards (waypointActual);
	}
	
	IEnumerator Wait(){
		animation.CrossFade ("idle");
		yield return new WaitForSeconds (5.0f);
		walk = true;
	}
	

	void comprobarDistancia(){
		// Elegimos un waypoint nuevo cuando ya se acerco lo suficiente al actual
		if (Vector3.Distance (transform.position, waypointActual) < 2.0f)
			selectNewWaypoint();
	}
	
	void selectNewWaypoint(){
        if (++wpElegido == listaWaypoints.Count) wpElegido = 0;
		waypointActual = (Vector3) listaWaypoints[wpElegido];

		walk = false;
    }
	
	void moveNPCTowards(Vector3 waypointActual){
	
		// Direccion: definimos el vector direccion hacia el cual nos vamos a mover (restamos posicion objetivo - posicion nuestra)
		Vector3 direccion = waypointActual - transform.position;
		// Movimiento:_Son "nuevas posiciones" cada vez mas cercanas al objetivo.
		// normalizar el vector es como obtener en lugar de toda la ruta, en que direccion tiene que dar "cada pequeño paso"
		// al multiplicar por la velocidad, determinamos que tan "largo" es ese paso.
		// usamos time.deltatime para que tenga un movimiento smooth respecto de los frames.
		
		Vector3 movimiento = direccion.normalized * velocidad * Time.deltaTime;
		
		transform.LookAt(waypointActual);
		transform.position = transform.position + movimiento;
		
	}
	

		
}

Since walk will be false for 5 seconds it will call the coroutine on update over and over again. Consider using a second flag to lock the call:

(not tested)

void Update () {
       comprobarDistancia();
       if (walk)
         Walk ();
       else if(!walk && !isWaiting){
         StartCoroutine(Wait());
       }
 
    }

IEnumerator Wait(){
       isWaiting = true;
       animation.CrossFade ("idle");
       yield return new WaitForSeconds (5.0f);
       walk = true;
       isWaiting = false;
    }

I may be missing something on WaitForSeconds usage, but what REALLY happens when reaching that instruction?

As far as I can see from the code you’ve modified, it will enter Wait(), then change state to “be waiting”. Then it gets the instruction to wait for five seconds. What does this insctruction mean?

A) The next frame will update() function be run or will it stop doing updates for five seconds?
B) Suppose we do not set isWaiting flag to true… will it get inside Wait() on every frame and execute instructions up to waitForSeconds instruction until 5 seconds pass? Or will it get inside the coroutine every time and re-start the waitforseconds on every frame , messing everything up?

I don’t imagine any other ways the code would be executed, but I just wanted to make sure to actually understand how you fixed it with this boolean.

Thanks!