
Simple coroutines for games in C++ - ingve
http://www.ilikebigbits.com/blog/2016/3/20/coroutines-for-games
======
derefr
Re: coroutines in games more generally, you can look to older console games
(NES/SNES-era) for some good examples. Earlier games on the C64 et al were—as
is commonly explained—purely event-loop based; but by the mid-1980s, the
paradigm had switched to using a hardcoded coroutine-scheduled actor model.

If you've never read it, the Super Mario Bros 1 disassembly[1] is extremely
enlightening. It feels a lot like Erlang, with individual parallel "process"
actors being responsible for updating the physics of each sprite on screen—but
instead of a "process" being an abstraction handled by a scheduler, it's just
an entry-point function that calls a finite-state-machine-state function, then
either calls a hardwired next "process" function, or a trampoline function
that calls whatever is left to run in that game state. Effectively, it's a
"direct-threaded next-jmp-prefetched" implementation of scheduling.

[1]
[https://gist.github.com/1wErt3r/4048722](https://gist.github.com/1wErt3r/4048722)

------
static_noise
Short version:

The coroutines are regular threads. The library provides an interface for
syncing which makes them behave as if they were "real" coroutines. This avoids
common problems you run into when making coroutines in C++ which by itself
does not support coroutines natively - but it supports threads very well.

Pros: Very easy to implement and use. Will likely work very well anywhere
without much thought.

Cons: Resource hungry. Not really designed for thousands of simultaneous
coroutines.

~~~
jasonjei
Question: couldn't you use OS fibers to do "coroutines" without being resource
intensive? I know all major OS kernels have supported fibers for some time...

~~~
gpderetta
Linux for one doesn't unless you count the obsolete (and slow) swapcontext.

~~~
acconsta
Google implemented fibers. Hopefully they'll get upstreamed.

[https://www.youtube.com/watch?v=KXuZi9aeGTw](https://www.youtube.com/watch?v=KXuZi9aeGTw)

~~~
gpderetta
If I remember correctly, that's an additional interface for usermode
initiated, cooperative scheduling of kernel threads (basically a variant of
pthread_yield that allows specifying which thread is going to get the
timeslice we are relinquishing).

It still requites a syscall and the entities being scheduled are proper kernel
level threads.

So not really fibers. Pretty cool though.

Edit: autocorrect

------
viperscape
Directed graphs are also a great way to create scripted dialogues and quests
for games. Visually, a DG for your game dev is quite powerful and makes it
very easy to reason about what's going on in the game story. I don't think the
same can be said for coroutines hard coded in c++. In addition, it's easy to
find a cycle in a graph, I'm not sure the same can be said for a coroutine.
Here's a site to give some interest:
[http://twinery.org/](http://twinery.org/)

~~~
vmorgulis
Very interesting. I can add Inform 7 and rule-based programming of text-based
fiction:

[http://eblong.com/zarf/essays/rule-based-
if/](http://eblong.com/zarf/essays/rule-based-if/)

[http://inform7.com/](http://inform7.com/)

------
jamesu
One big problem with using coroutines for "scripting" long running events in
games I don't often see mentioned is what to do - if anything - about saving
their state. For cutscenes this can be ignored for sure, but what if you tie
something like a long conversation tree into a coroutine?

For this example library if your making some sort of traditional adventure
game and you wanted the player to be able to save the game you'd have to
restrict saving to checkpoints, use a loop with a switch (practically moving
back to the state machine approach), or just split the conversation up into
multiple coroutines as it progresses.

And of course, there's the whole "what to do when you need to patch the game"
problem on top of all that.

~~~
arianvanp
There was a discussion in the /r/haskell subreddit about serialisable
coroutines a week back:
[https://www.reddit.com/r/haskell/comments/4a8wr8/how_to_get_...](https://www.reddit.com/r/haskell/comments/4a8wr8/how_to_get_serializable_coroutines/)

Perhaps it helps

------
derFunk
Some time ago (10 years) we implemented this as 'continuations' into our own
game engine. E.g. see [https://felipec.wordpress.com/2008/09/28/continuations-
in-c-...](https://felipec.wordpress.com/2008/09/28/continuations-in-c-easy-
asynchronous-stuff/) and
[http://c2.com/cgi/wiki?ContinuationImplementation](http://c2.com/cgi/wiki?ContinuationImplementation)

------
fsloth
Lightweight abstractions for control flow are a good idea. Animation control
using threading primitives is not a good idea.

------
jestar_jokin
I recommend having a look at the source code for ScummVM. The original SCUMM
engine, first used in Maniac Mansion, implemented its own VM with cooperative
multithreading, way back on the C64.

Basically, the game ran "scripts" (containing custom bytecode), each given its
own "slot" with space for locals. Each script is run in turn, and each script
must voluntarily relinquish control when appropriate.

Of course, this is a bit more advanced than simple coroutines. If you're going
to the effort of creating your own embedded scripting language, you may as
well an existing language like Lua.

Example code:
[https://github.com/scummvm/scummvm/blob/master/engines/scumm...](https://github.com/scummvm/scummvm/blob/master/engines/scumm/script.cpp)

------
AnkhMorporkian
Quick question for you C++ gurus: Why doesn't C++ have native generators? Is
there some technical reason that makes it hard to compile such a feature, or
is there a political reason?

~~~
quotemstr
Plenty of lightweight thread systems exist for C++. You're thinking about the
problem the wrong way. You don't really want _generators_ : you want _fibers_.
Once you have fibers, generators are trivial to implement. One implementation
of fibers for unixlike systems is GNU nPth.

~~~
AnkhMorporkian
Not exactly. I'm not familiar with nPth specifically, but it appears it uses
swapcontext, which is a _lot_ slower than doing it in an event loop the proper
way. Like, 1000 times slower (on Linux)[1]. Having proper generators and
extendable event loops is a far cleaner solution to the problem, in my humble
opinion. Python's asyncio is perhaps the best implementation of such an event
loop I've seen.

1:
[http://www.crystalclearsoftware.com/soc/coroutine/coroutine/...](http://www.crystalclearsoftware.com/soc/coroutine/coroutine/fibers.html)

~~~
gpderetta
nPth can use swapcontext if everything else fails, but its preferred portable
switching strategy is the so called sigaltstack trick witch is usually
significantly faster.

BTW, I'm the author of the above page. Coroutine libraries have been my
personal hobby since forever.

~~~
srean
In that case you might enjoy taking a look at

[http://felix-lang.org/](http://felix-lang.org/)

[http://felix-
lang.org/$/usr/local/lib/felix/felix-2016.01.04...](http://felix-
lang.org/$/usr/local/lib/felix/felix-2016.01.04-rc2/share/src/web//tut/fibres_index.fdoc)

I am not involved in its development.

------
seivan
Behaviour Tree's would be easier to model and write with existing libraries
that implement the decorators (wait for, etc)

