Originally I thought coroutines in Unity were exclusively for performing asynchronous operations, like
AssetBundles.LoadAsync(). However when I was planning the UI for my new game, the most wise Eddie and Kalin told me about using coroutines to simulate a state machine. It took me a while to understand how this worked conceptually, and then how to implement it. I couldn’t find any particularly good tutorials either.
This is more of a series of annotated snippets than a full tutorial, but hopefully it will be useful for someone.
The important thing to remember with this is that we’re emulating a finite state machine. If you’re unfamiliar with FSMs, read up on them before starting this and sketch out a few FSMs for games you understand, thinking about what possible transitions there are between states, and what setup/teardown each state does.
With that out of the way, let’s look at some snippets!
The first snippet is a template of what we’ll be doing with our UI-related coroutines. There’s not much to say about it beyond what’s in the comments.
Basic Pause Menu
Our second snippet has some code that actually does something useful.
The most important line in this snippet is
yield return StartCoroutine(DoShowPause()). Here we start the pause coroutine, and importantly pause the Start method at that line until the DoShowPause coroutine finishes. As we are simulating a finite state machine, it will only finish when it exits the state.
One convention I like to use with coroutines is to prefix all coroutine method names with Do. The reason for this is if you call a coroutine without wrapping it in
StartCoroutine() the coroutine stops at the first yield, but no warnings or errors are logged. By using a consistent naming convention, it’s easy to notice naked
As in the comments make sure you include
yield return null somewhere in your while loops or Unity will appear to freeze as it goes into an uninterrupted loop.
Player/Enemy Turn System with Pause
The third and final snippet for this tutorial is a very simple simulation of a turn-based game. It has alternating Player/Enemy turn states, and a pause state that can be called from either.
This snippet illustrates the possibility of having “global” states that can be called from any state. However you explicitly have to check for the trigger to enter the global state at a safe point in each state.
As you can see even with a simple situation, the code can get fairly long. In a more complicated game it would quickly become worthwhile to split up the code for each state into its own class.
So that’s a simple example of how you’d use coroutines to set up a FSM for menus. You can jump in and out of states easily and be sure which state you’re in.