1
\$\begingroup\$

I want to display the wave number before every incoming wave of enemies. And after displaying this it should hide the text component. I tried to get this done with StartCoroutine() and Ienumerator but that does not seem to work as it runs only once. As in Wave 1 is displayed and nothing after wards.

Below is the script I am using for this:

public class WaveUpdateTextScript : MonoBehaviour
{

    [SerializeField]
    WaveSpawner spawner;

    private WaveSpawner.SpawnState currentState;
    private Text waveDisplay;

    // Use this for initialization
    void Start()
    {
        waveDisplay = GetComponent<Text>();

        StartCoroutine(WaveDisplaySwitch());

    }


    IEnumerator WaveDisplaySwitch()
    {

        switch (spawner.NextWave)
        {
            case 1:
                waveDisplay.text = "Wave 1 of 8 !!";
                yield return new WaitForSeconds(4f);
                gameObject.SetActive(false);
                break;
            case 2:
                gameObject.SetActive(true);
                waveDisplay.text = "Wave 2 of 8 !!";
                yield return new WaitForSeconds(4f);
                gameObject.SetActive(false);
                break;
            case 3:
                gameObject.SetActive(true);
                waveDisplay.text = "Wave 3 of 8 !!";
                yield return new WaitForSeconds(4f);
                gameObject.SetActive(false);
                break;
            default:
                Debug.Log("This be the meh wave...");
                break;
        }

    }
}

I know whats going on, on start() the coroutine is started once and then case 1 of switch works and then stops at break.

How would I solve this? or is this not the best way of achieving the desired result?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

You've halfway got it. You've noticed that you kick off the coroutine but only evaluate it once, so you need to introduce a mechanism that will trigger multiple times.

There are two major ways to do this. You can either have your coroutine spin in an infinite loop, checking at regular intervals for a change in wave, or you can have a function that some other piece of code calls every time a new wave begins.

Option 1:

public class WaveUpdateTextScript : MonoBehaviour
{

    [SerializeField]
    WaveSpawner spawner;
    private Text waveDisplay;

    void Start()
    {
        waveDisplay = GetComponent<Text>();
        StartCoroutine(WaveDisplaySwitch());
    }


    IEnumerator WaveDisplaySwitch()
    {
        int currentWave = -1;
        while(true)
        {
            if(currentWave != spawner.NextWave)
            {
                waveDisplay.text = "Wave " + spawner.NextWave + " of " + spawner.WaveCount + "!!";
                waveDisplay.gameObject.SetActive(true);
                yield return new WaitForSeconds(4f);
                waveDisplay.gameObject.SetActive(false);

                currentWave = spawner.NextWave;

                break;
            }
            //else if currentWave == spawner.NextWave, then the wave hasn't changed since the last check, so we do nothing.

            //Ends the infinite loop if we've hit the last wave
            if(currentWave == spawner.WaveCount)
                break;

            //This will make the loop check once per second, which can be adjusted as needed.
            yield return new WaitForSeconds(1.0f);
        }
    }
}

Note that this option assumes that NextWave is an integer of some kind, incremented by some other code that determines that the wave has completed. This also assumes that something else is managing spawner.NextWave, such as incrementing it whenever all enemies have despawned.

Option 2:

public class WaveUpdateTextScript : MonoBehaviour
{

    [SerializeField]
    WaveSpawner spawner;
    private Text waveDisplay;

    void Start()
    {
        waveDisplay = GetComponent<Text>();
    }

    public void StartNewWave(int wave)
    {
        StartCoroutine(WaveDisplaySwitch(wave));
    }

    IEnumerator WaveDisplaySwitch(int wave)
    {
        waveDisplay.text = "Wave " + wave + " of " + spawner.WaveCount + "!!";
        waveDisplay.gameObject.SetActive(true);
        yield return new WaitForSeconds(4f);
        waveDisplay.gameObject.SetActive(false);
    }
}

This method is much simpler. Some other code has to manage the wave spawning anyway, right? So just have that code call StartNewWave in addition to incrementing the wave number and whatever other bookkeeping it has to do.

\$\endgroup\$
2
  • \$\begingroup\$ Thank you. You're right option 2 was much simpler and thats what I decided to go with. I had to make a minor change. I moved waveDisplay.gameObject.SetActive(true); before calling the coroutine as I would get the error, could not start coroutine as gameobject is disabled. Thanks again :) \$\endgroup\$ Apr 12, 2018 at 22:03
  • 1
    \$\begingroup\$ @StuckInPhD Good catch. It's for that reason that I tend to nest game objects: put the WaveUpdateTextScript on one object and the Text itself on a separate child gameobject, referenced as a field. That way you can enable/disable visual elements without impacting the processing of the script itself. \$\endgroup\$
    – ketura
    Apr 12, 2018 at 22:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .