Making object wait when moving between waypoints

Hi,

I’m trying to make an object wait at its current location for a second or two before moving on the its next location. Applying a coroutine didn’t achieve the desired effect, maybe I’m just using it wrong. Please advise. Thanks.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyProperties : MonoBehaviour
{
    [Header("Movement Properties")]
    public Transform[] waypoints;
    public float speed;
    public int curWaypoint;
    public bool patrol = true;
    public Vector3 target;
    public Vector3 moveDirection;
    public Vector3 velocity;
    public float entryWait = 3f;
    public float positionWait = 1f;

    void Awake()
    {
        StartCoroutine(EntryWait());
    }

    void Update()
    {
        if (curWaypoint < waypoints.Length)
        {
            target = waypoints[curWaypoint].position;
            moveDirection = target - transform.position;
            velocity = GetComponent<Rigidbody>().velocity;

            if (moveDirection.magnitude < 1)
            {
                curWaypoint = Random.Range(0, 14);
            }
            else
            {
                velocity = moveDirection.normalized * speed;
            }
        }
        else
        {
            if (patrol)
            {
                curWaypoint = 0;
            }
            else
            {
                velocity = Vector3.zero;
            }
        }

        GetComponent<Rigidbody>().velocity = velocity;
    }

    private IEnumerator EntryWait()
    {
        curWaypoint = 7;
        yield return new WaitForSeconds(entryWait);
    }
    private IEnumerator PositionWait()
    {
        yield return new WaitForSeconds(positionWait);
    }
}

Hi!

So, it’s important to understand what Coroutines do and what they don’t.


You should always remember that they are not creating delay for your Update() function - look at this script:

void Update()
{
    StartCoroutine(WaitBeforeDo());

    Debug.Log("I am doing something");
}

IEnumerator WaitBeforeDo()
{
    Debug.Log("Wait before do");

    yield return new WaitForSeconds(5f);
}

Theoretically we want to create delay before debugging “I am doing something”, but…oh, look at the output in the console:160056-ex1.png
As we can see we not only continously calling the “I am doing something”, but also “Wait before do”, because it gets called each frame as well.

Therefore you shouldn’t think of coroutine as time freezer for your main executive function, but better as an function with option to switch variables not at the same frame, but with given delay.


Now look at this code:

bool isDoingSomething = true;
float timer = 0f;

void Update()
{
    if (isDoingSomething)
    {
        Debug.Log("I am doing something");
        timer += 1 * Time.deltaTime;

        if (timer > 2f)
        {
            isDoingSomething = false;
            StartCoroutine(WaitBeforeDo());
        }
    }

}

IEnumerator WaitBeforeDo()
{
    // Action BEFORE delay
    Debug.Log("Wait before do");

    // Delay itself
    yield return new WaitForSeconds(3f);

    // Action AFTER delay
    isDoingSomething = true;
    timer = 0f;
}

And let’s take a look at the console:
160057-ex-2.png
Great, now it’s working.

The “I am doing something” gets called multiple times, but when we receive “Wait before do” it freezes for couple of seconds and then starts again, but only because our Coroutine swaps isDoingSomething boolean after delay, not because of the delay itself.


As you can see we now have much more variables in our code itself, so the first thing for Coroutines to be useful is proper design of your executive functions - you should have variables that will be adjusted by the Coroutine before/after given delay or otherwise you call functions with delay, but nothing changes after the delay.


So, the overall look that you would like to achieve could look like this:

bool isMoving = true;

    void Update()
    {
        if (isMoving)
        {
            // Move code;

            if(/* We reach destination or any conditions that you want to achieve before waiting*/)
            {
                isMoving = false;   // Stops move code execution;
                StartCoroutine(PositionWait());     // Calls the Coroutine
            }
        }
    }

    IEnumerator PositionWait()
    {
        yield return new WaitForSeconds(positionWait);  // The Coroutine will wait for given seconds
        isMoving = true;       // Only after given seconds gone it will switch boolean to true so the Update() functions will starts execute code inside if (isMoving) statement
    }

I hope it helps.