Herb
Herb

Reputation: 171

Switch inside IEnumerator keeps looping

Hi folks I have this little 2D guy who has 2 states "IsSpeaking" and "Silence" I use them to make the Animator set a bool to true or false and the character seems to be speaking.

I need that the IsSpeaking lasts a different amount of time depending on the amount of text that is displayed on screen like this:

Text dialogueNumber(X) appears
Animate Speaking
Wait "X" time...
Animate Idle
Wait in Idle for next text (dialogueNumber) to appear and restart cycle...

I tried to do this with this little code:

void Update () {
 StartCoroutine(IsSpeaking());
}

IEnumerator IsSpeaking()
{
    switch (dialogueNumber)
    {
        case 0:

            yield return null;                              

            break;        

        case 1:

            functionsScript.IsSpeaking();
            yield return new WaitForSeconds(3);
            functionsScript.Silence();                
            yield break;                

        case 2:

            functionsScript.IsSpeaking();
            yield return new WaitForSeconds(6);
            functionsScript.Silence();                
            yield break;
    }
    yield break;

So the longer the text the longer the Wait time to switch to the silence animation but it seems works fine the first time (case 1) and after it goes to silence it keeps going going back and forth randomly from IsSpeaking to Silence, I'm not sure if this was the best way to achieve this or if there's a better way to switch between states and wait an amount of time?

Thanks a lot for the advice...

Upvotes: 0

Views: 1444

Answers (3)

Herb
Herb

Reputation: 171

Indeed my problem had a bad approach it was easier to make this outside the update function, turned out to be easier to set the number in the same "Next" button as a function called inside the On Click() resulting in something like this:

     public void TextID()
       {
         textID= (int)VD.nodeData.extraVars["cuadro"];
         print(textID);

             switch (textID)
             {
               case 0:
               break;

               case 1:
               StartCoroutine(LipsMoving(3));
               break;

               case 2:
               StartCoroutine(LipsMoving(6));
               break;
             }

        }

        IEnumerator LipsMoving(int talkTime)
         {
          funcionesScript.Speaking();
          yield return new WaitForSeconds(talkTime);
          funcionesScript.Idle();

          yield return null;
         }

In this way I get the frame ID as the user clicks the button and also runs my coroutine without weird loops. Thanks again for the help and patience it's refreshing to have different points of view to find the right way, hope this is useful to you guys

Upvotes: 0

TheSkimek
TheSkimek

Reputation: 342

Wouldnt it be better to let the player decide when the next dialogue appears? Not everyone reads the same speed.

Something along the line (hope thats not horribly on the wrong track):

while(!ExitDialogue)
{
    animation.Play("Idle");
    yield return StartCoroutine(IdleUntilNext()); //waits for player to press the defined button _keyCode_
    diaNumber++;
    animation.Stop("Idle");
    animation.Play("Speak");
    StartCoroutine(SpeakDialogue(diaNumber)); //wait for X seconds depending on diaNumber before stopping the animation
    animation.Stop("Speak");

}


IEnumerator SpeakDialogue(int diaNumber)
{
    switch (diaNumber)
    {
        case 1:
            yield return new WaitForSeconds(3);
            yield break;
        case 2:
            yield return new WaitForSeconds(6);
            yield break;
        default:
            ExitDialogue = true;
            yield break;
    }


}

IEnumerator IdleUntilNext()
{
    while(!Input.GetKeyDown(_keyCode_))
    {
        yield return null;
    }   
}

Upvotes: 1

Programmer
Programmer

Reputation: 125435

You are calling IsSpeaking every frame in the Update function which means it will be started over and over again even when the other one is not yet finished due to WaitForSeconds. Your current code could literately spawn over 400 coroutines running at- theme time and also trying to "speak" and "silence" at the-same time.

You can use a simple boolean variable to fix that issue.

bool speaking = false;

void Update()
{
    //Run only if not running
    if (!speaking)
        StartCoroutine(IsSpeaking());

}

Then set the boolean variable to false before breaking out of the switch statement in the coroutine.

IEnumerator IsSpeaking()
{
    speaking = true;

    switch (dialogueNumber)
    {
        case 0:

            yield return null;

            speaking = false;
            break;

        case 1:

            functionsScript.IsSpeaking();
            yield return new WaitForSeconds(3);
            functionsScript.Silence();

            speaking = false;
            yield break;

        case 2:

            functionsScript.IsSpeaking();
            yield return new WaitForSeconds(6);
            functionsScript.Silence();

            speaking = false;
            yield break;
    }
    yield break;
}

Upvotes: 5

Related Questions