Search code examples
unity-game-enginegame-enginecoroutine

Unity Coroutine Not Waiting


I am making a game where I want an overlay to appear for a specified amount of seconds before disappearing. I am using a Coroutine to do this, but it is now working. Is there a way to fix this issue or should I do it another way. I don't get any errors, but the coverObject doesn't display for 3 seconds and hide for 2 seconds as specified in the code.

The code:

void playRound()
{
    coverObject.gameObject.SetActive(true);
    for (int i = 0; i < keys - 1; i++)
    {
        GameObject instantiated = Instantiate(key);
    }
    waitSecs(3);
    coverObject.gameObject.SetActive(true);

    GameObject instantiatedCorrect = Instantiate(key);
    correctKey = instantiatedCorrect;

    waitSecs(2);
    coverObject.gameObject.SetActive(false);
}
    
void waitSecs(int seconds)
{
    StartCoroutine(WaitSecsCoroutine(seconds));
}

IEnumerator WaitSecsCoroutine(int seconds) 
{
    yield return new WaitForSeconds(seconds);
}

Solution

  • your method playRound will keep running while the WaitSecsCoroutine coroutine is yielding. This is because control is returned to the calling method as soon as a yield return statement is reached.

    The following is happening:

    • playRound is called
    • your GameObjects get instantiated
    • waitSecs is called
    • waitSecs calls to coroutine waitSecsCoroutine
    • waitSecsCoroutine reaches the yield return new WaitForSeconds()
    • Control is now returned back to waitSecs, which in turn returns to PlayRound
    • playRound continues with coverObject.gameObject.SetActive(true); and the subsequent code while waitSecsCoroutine is still waiting.

    What you need to do is make playRound a coroutine as well, and use yield return WaitSecsCoroutine so the function halts its execution until WaitSecsCoroutine is finished:

    IEnumerator playRound()
    {
        coverObject.gameObject.SetActive(true);
        for (int i = 0; i < keys - 1; i++)
        {
            GameObject instantiated = Instantiate(key);
        }
        yield return WaitSecsCoroutine(3);//This now waits until WaitSecsCoroutine is finished
        coverObject.gameObject.SetActive(true);
        GameObject instantiatedCorrect = Instantiate(key);
        correctKey = instantiatedCorrect;
        yield return WaitSecsCoroutine(2);
        coverObject.gameObject.SetActive(false);
    }
    

    Note however that you can also just call the WaitForSeconds directly from your playRoudn method too once it is a coroutine, without needing a seperate function

    IEnumerator playRound()
    {
        coverObject.gameObject.SetActive(true);
        for (int i = 0; i < keys - 1; i++)
        {
            GameObject instantiated = Instantiate(key);
        }
        yield return new WaitForSeconds(3);
        coverObject.gameObject.SetActive(true);
        GameObject instantiatedCorrect = Instantiate(key);
        correctKey = instantiatedCorrect;
        yield return new WaitForSeconds(2);
        coverObject.gameObject.SetActive(false);
    }
    

    Alternatively you could move the code you want to execute after the wait into your WaitSecsCoroutine method.