How to break out of loop in coroutine after some player input?

10.5k views Asked by At

What I need is to increment player stats every few second and be able to interrupt this process with any key pressed down.

Below is my coroutine. It's running endlessly, any advice?

IEnumerator PlayerAction(PlayerStats playerStats, int incrementor1) 
{
    bool loop = true;
    while (loop)
    {
        if (Input.anyKeyDown)
        {
            loop = false;
            Debug.Log("Action is break");
            yield break;
        }
        else
        {
            yield return new WaitForSeconds(2);
            playerStats.Energy += incrementor1;
            GameClock.Tic++;
            Debug.Log("My first courutine is working!");
        }
    }
}
2

There are 2 answers

1
Programmer On

It's running endlessly, any advice?

Yes. Don't use yield return new WaitForSeconds to wait. You will miss the Input event (if (Input.anyKeyDown)) during this frame.

Another way to make counter in a coroutine function is to use Time.deltaTime; to increment a variable, then wait with yield return null; which only waits for a frame instead of seconds, when you use yield return new WaitForSeconds.

This is what I explained above should look like:

IEnumerator PlayerAction(PlayerStats playerStats, int incrementor1)
{
    const float secToIncrement = 1f; //When to Increment (Every 1 second)
    float counter = 0;

    while (true)
    {
        //Check if we have reached counter
        if (counter > secToIncrement)
        {
            counter = 0f; //Reset Counter

            //Increment Player stats
            playerStats.Energy += incrementor1;
            GameClock.Tic++;
            Debug.Log("My first courutine is working!");

        }

        //Increment counter
        counter += Time.deltaTime;

        //Check if we want to exit coroutine
        if (Input.anyKeyDown)
        {
            Debug.Log("Action is break");
            yield break;
        }

        //Yield in a while loop to prevent freezing 
        yield return null;
    }
}

Make sure to call a coroutine with the StartCoroutine function like StartCoroutine(PlayerAction(yourPlayerStat,2));

0
Muhammad Faizan Khan On

I guess your are missing the input detection in coroutine, my advice to detect input in update and used a variable to check it, something like below:

bool isAnyKeyDown = false;//detect is anykeypressed

void Update(){
  if (Input.anyKeyDown){
          isAnyKeyDown = true;
   }

}

IEnumerator PlayerAction(PlayerStats playerStats, int incrementor1) 
{
    bool loop = true;
    while (loop)
    {
        if (isAnyKeyDown)
        {
            loop = false;
            Debug.Log("Action is break");
            StopCoroutine("PlayerAction");
            isAnyKeyDown = false;
            yield break;
        }
        else
        {
            yield return new WaitForSeconds(2);
            playerStats.Energy += incrementor1;
            GameClock.Tic++;
            Debug.Log("My first courutine is working!");
        }
    }
}