Wafflebasket
Wafflebasket

Reputation: 63

Unity 'StartCoroutine' does not run twice when IEnumerator is passed as a variable?

So I've ran into something weird, when using a co-routine in Unity to simulate a NPC (walks towards a target, idles for x seconds, walks to a target --repeat--).

I've found that starting the co-routine with a variable that holds the IEnumerator will not run twice, while starting the co routine with the method passed in directly runs as expected, repeatable.

Why does this work this way? Whats happening 'under the hood'? I cant wrap my head around why this is, and it bugs me.

Below my IEnumerator method that simulates idle time.

private IEnumerator sitIdle()
{
    var timeToWait = GetIdleTime();
    _isIdle = true;
    yield return new WaitForSeconds(timeToWait);
    _isIdle = false;
} 

If this gets called a second time per Scenario #1 (below), it runs as expected when called multiple times. It just repeats the process over and over.

If however, if it gets called per Scenario #2 (below) as a variable, it will kick off once, but refuse to enter a second time and just plain 'skip' it in code.

void LateUpdate()
    {
        _idleRoutine = sitIdle; //this is not actually in the late update, just moved here for reference.

        if (_agent.hasPath)
        {
            if (isTouchingTarget())
            {
                StartCoroutine(sitIdle2()); //Scenario #1

                StartCoroutine(_idleRoutine); //Scenario #2

                _currentTarget = null; 
                _agent.ResetPath();
            }
        }

Tl;dr: StartCoroutine(variable to IEnumerator) is not repeatable, while StartCoroutine(IEnumerator()) works fine, why cant I pass the IEnumerator as a variable?

Upvotes: 2

Views: 1533

Answers (1)

Ruzihm
Ruzihm

Reputation: 20259

The return value of SitIdle() is an IEnumerator. IEnumerators do not repeat once they are completed and have no iterations remaining, which is what StartCoroutine tells Unity to do. Each time the coroutine is resumed at a yield is another iteration Unity tells the coroutine to perform.

If you really would like to store the coroutine as a variable, so you can choose between one or another, you could store the method you're interested in as a delegate, then call that to get your fresh IEnumerator:

IEnumerator sitIdle()
{
    var timeToWait = GetIdleTime();
    _isIdle = true;
    yield return new WaitForSeconds(timeToWait);
    _isIdle = false;
}  

IEnumerator sitIdleAlternative()
{
    var timeToWait = GetIdleTime() + 2f;
    _isIdle = true;
    yield return new WaitForSeconds(timeToWait);
    _isIdle = false;
}

delegate IEnumerator IdleDelegate ();

IdleDelegate _idleRoutine;

void LateUpdate()
{
    _idleRoutine = new IdleDelegate(sitIdleAlternative); //this is not actually in the late update, just moved here for reference.

    _idleRoutine = new IdleDelegate(sitIdle);

    if (_agent.hasPath)
    {
        if (isTouchingTarget())
        {
            StartCoroutine(sitIdle2()); //Scenario #1

            StartCoroutine(_idleRoutine()); //Scenario #2

            _currentTarget = null; 
            _agent.ResetPath();
        }
    }
}

Upvotes: 3

Related Questions