Leave a comment

Deeply-nested coroutines in Unity

In Unity, coroutines are essentially methods that execute across several frames. They are, in a sense, some syntactic sugar that lets you avoid having to keep track of the state of a sequence of operations (for instance, fading from one color to another) in member variables of a behavior. Instead, all state can be method-local. Refer to the Unity documentation for the basics around coroutines.

For a recent project (A Unity “plugin” that runs old Sierra games – or at least those created with my SCI Companion), I needed to have an interpreter running that processes byte code – basically a game engine within a game engine. In its original (non-Unity) form, I ran the interpreter on a separate thread. This simplified things in some sense, as I could pause and continue this thread in order to pause or continue the interpreter (e.g. for stepping through byte code instructions one by one). In addition, the actual callstack of this thread could be used to store the actual callstack of the interpreter. The interpreter doesn’t only process byte-code – it can call through to “kernel” functions that I implement. And those in turn can call back into byte-code.

Supporting something like this on the same thread on which the “game engine within a game” is running would be extremely difficult, as control needs to return from the Update part of the game loop on each frame (for something like a standard Windows application, this wouldn’t be as difficult, as we can pump messages and process input at any point).

When I ported this project to Unity, I kept this architecture. However, WebGL was one of my target platforms – and javascript/html5 doesn’t support threads (currently). Indeed, building my project for WebGL verified this.

So I was left with the task of figuring out how to switch my architecture over to using coroutines – or figuring out if it was even possible! Remember, the callstack in my interpreter can be arbitrarily deep, and I really didn’t want to make every function coroutine aware.

The remainder of this post will summarize how I managed to accomplish this.

Requirements

I need the following things:

  • Some of my interpreter’s kernel functions need to yield (return control to Unity’s update loop, as they pause indefinitely).
  • Some of them need to yield conditionally (only return control sometimes).
  • My individual byte-code instructions need to conditionally yield also, since they may call into kernel functions.
  • If my in-game debugger is active, then of course I need to be able to potentially yield on each instruction processed.

Here’s the plan I came up with. First:

There is only a single coroutine – a top level “RunInterpreter” function that returns an IEnumerator.

It, in turn, calls into the rest of my interpreter code (specifically, it is a loop that processes byte code instructions). All other functions that either return control – or call into other functions that return control – need to return IEnumerable (not IEnumerator). Yes, this does mean that pretty much everything in my interpreter needs to be “coroutine aware”. That ended up not being a huge deal for the most part though. The trickiest bit was deciding how to return values from functions now that they must return IEnumerable.

For functions that clearly need to yield control (such as the Wait kernel, which waits for a specified number of real world milliseconds), or when the in-game debugger is broken into the interpreter, we simply need to do:

yield return null;

Now, I also had a situation where a virtual function was called. Some overrides needed to yield, and some didn’t. Because of the potential need to yield, the virtual function signature had to have a return type of IEnumerable. So what about those overrides that should not yield? A function that returns IEnumerable in C# won’t compile unless there is a yield somewhere in it. The solution is to use yield break:


// Pushes a value onto the stack
class push : Instruction
{
public override IEnumerable Do(byte opcode)
{
context.Push(context.Acc);
    yield break; // Don't yield at all!
}
public override byte Opcode { get { return 0x36; } }
}

This indicates that the enumeration has terminated – so basically the function will just return an empty enumerable. This is actually what happens at the end of a IEnumerable function which doesn’t end with a yield – there’s basically an implicit yield break there. From this, we can gather that in any cases where you had an early return from a function, yield break is also what you should use.

Now what about functions that call into other functions that might yield (i.e. other functions that return IEnumerable). For instance, in my project there is a callk byte-code instruction that calls a kernel function. A kernel function (such as Wait) might yield, so that needs to be propagated out. In that scenario, I iterate over the returned IEnumerable and yield each element:


foreach (var guy in interpreter.Kernel.Invoke(index, numParams, context))
{
yield return guy;
}

Note that in the case where the kernel function didn’t yield at all (which is the majority of the cases), an empty enumerable is returned. This means yield return guy is never hit, and we won’t yield control at this point.

This is important, as we definitely don’t want to accidentally yield control. Doing so would suddenly introduce a 16ms (or whatever your fixed timestep is) wait before we continued execution of the interpreter.

That’s pretty much it. Once you get the hang of how yield works, it ends up being pretty straightforward.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Space Quest Historian

Let's Play's, Podcasts, and General Adventure Game Goodness

Harebrained Schemes

Developer's blog for IceFall Games

kosmonaut games

Development blog of "Bounty Road"

Halogenica

Turn up the rez!

bitsquid: development blog

Developer's blog for IceFall Games

Game Development by Sean

Developer's blog for IceFall Games

Lost Garden

Developer's blog for IceFall Games

Memories

Developer's blog for IceFall Games

Casey's Blog

Developer's blog for IceFall Games

Blog

Developer's blog for IceFall Games

Rendering Evolution

Developer's blog for IceFall Games

Simon schreibt.

Developer's blog for IceFall Games

Dev & Techno-phage

Do Computers Dream of Electric Developper?

- Woolfe -

Developer's blog for IceFall Games

Ferrara Fabio

Game & Application Developer, 3D Animator, Composer.

Clone of Duty: Stonehenge

First Person Shooter coming soon to the XBOX 360

Low Tide Productions

Games and other artsy stuff...

BadCorporateLogo

Just another WordPress.com site

Sipty's Writing

Take a look inside the mind of a game developer.

Jonas Kyratzes

Writer, game designer, filmmaker.

%d bloggers like this: