Now, let me start this post by saying I love XNA/Monogame. XNA was Microsoft’s first (and currently last) attempt to have a first class managed framework for writing games. Luckily the excellent Monogame project has picked up Microsoft’s slack and put XNA on Mac, Linux, iOS, Android, PS Vita, Windows 8, and more.

I remember back in the late 90’s writing DirectX 7 code in C++ and the ridiculous amount of boilerplate associated with it – setting up a Win32 Window, initialising DirectDraw, initialising Direct3D, doing voodoo incantations and maybe after a couple of hours of tinkering you’d have an empty window. XNA just made it easy to create a new project and start writing a game in seconds.

But, as good as XNA is, it’s still rooted in mid-noughties Microsoft OO conventions. Which means that using it in modern languages makes it feel somewhat awkward.

Now, if you want to follow along at home, you’re going to need XNA for a modern Visual Studio (as MS no longer support it officially in the newer IDEs), and you’re going to need this pure F# XNA Template. You’ll need to install XNA before trying to create a new application in VS.

Code Sample – Version 1

Upon creating a new project using that template, you’re going to see a simple XNA Game implementation, using idiomatic XNA conventions.

Most F# developers are probably feeling a bit ill at this point. But to drive home what I hope to fix, let’s highlight the obvious:

Mutable state everywhere! (x,y,dx,dy, sprite, spriteBatch)

Therefore null can be almost everywhere! (sprite, spriteBatch) – even option types wouldn’t help us that much here.

Given there’s so much mutation, it’s hard to tell what is mutable for what reason – x, y, dx and dy are the game state, spriteBatch and sprite are framework code that have to be initialised at specific points after the constructor.

XNA prefers if the Update and Draw cycles are decoupled, so in order to carry state between the 2 calls the framework really enforces mutation through every part of it’s design, leaving the developer little choice in the way of choice.

F# to the rescue (and a WPF diversion)

One of the things I like most about F# is the ability to take a problem in one programming model and solve it in another one. in this small WPF example, we transform from a traditional event based model to async workflow, which makes the code look entirely synchronous, and changes the model from event driven to recursive loops.

The addLine async workflow is where the magic happens – we await our 1st triggering event (in this case a mouse down), capture some information, then await our exiting event (in this case a mouse up), perform the action (draw the line), then do start again.
The C# equivalent, would likely have ended up capturing the intermediate state between events a class level mutable fields, or worse, some shoehorned in Rx “goodness”.

One of the most common misconceptions I hear about async (in both C# and F#) is that it’s about threads – I prefer describe it as simply “non-blocking” – notice this WPF example has nothing to do with threads, despite relying on async.

Therefore async can be used to help simplify code, even if it has nothing to do with threading.

Back to XNA

Back in XNA, we don’t have events already exposed like WPF. But we can add them, and trigger them at the keys points – Initialise, LoadContent, and then in a loop over Update and Draw.

We can then expose the events as async Workflows, and then consume the game logic entirely outside the XNA Game implementation. Notice that now the gameLoop() workflow has no explicit mutation – each frame is awaited, rendered and then the state accumulates into the next update.

Using async workflows we’ve managed to move all the framework related code into it’s own place (the Game class) and the essentially syncronous application flow into an async workflow.

This works great in this sample, but keen XNA game developers will notice that in this sample the Update and Draw are in lockstep with each other – this isn’t always the case. Now we’ve transformed our mutation-heavy, frame-based application and turned it a simple recursive loop with accumulating state, we can tackle this final problem and call this a success.

If Update and Draw both trigger the same event, but differentiated using different cases of a Discriminated Union, we can safely await the single event, and act appropriately. Note that IsFixedTimeStep is now set to false.

Conclusion

XNA/Monogame is a great tool for writing games, and F# offers many benefits to game developers. However the 2 technologies working together do have some rough edges, which can be transformed into advantages using some basic features of F#.

Features such as F# async empower developers to find ways to tame the accidental complexity, introduced by external frameworks such as XNA or WPF.

Ryan – glad you found it useful! That’s really cool how you’ve made it work in Unity. Do you have any good pointers on making F# work in Unity? I’ve always been intrigued by it, but the thought of using C# always makes me feel slightly off.

I’m curious to hear your thoughts about how you would replicate this in C# using async/await. I’d be really interested in baking a template for this and throwing it on GitHub to spread the architectural goodness of this approach. This is a problem that I’ve been mulling over for the past week now and after a few hours of digging, I found this. Would love to chat, as I’ve got some other problems I’m working with that build on top of this that I’m not sure of the best approach.

Hopefully you enjoyed the follow on post – did that answer your question? I think compared the F# version the advantages are less clear, but there is still a transformative effect from a frame based system to a recursive loop, even if some of the purity of immutability is not present.

If you have any thoughts or would like to discuss further, DM me on twitter and I’d be happy to exchange emails.

Thanks for this interesting approach, but I cannot quite get the final version to work. For some reason, ‘nextState’ ALWAYS matches Update even though I see that ‘loopEvt.Trigger (Draw gameTime)’ is firing… I also copy-pasted the code from the Github repo to no avail. Any idea what might be wrong?

Hi – I’ve take a quick look and I’m a bit baffled. Under XNA I get desired behaviour. Removing references to XNA and updating to Monogame DirectX and as you say – black screen. Switching to Monogame OpenGL, and it works again.

My 1st hunch was that DX was using 2 threads, but all versions use 1 thread.
My 2nd hunch was around SynchronizationContexts not being set up, but they are all present and correct.
My 3rd hunch was timing playing a factor, so a couple of attempts to stagger the draw/update more – no result.
My final hunch – dont use Async.AwaitEvent and switch in Tomas Petricek’s AwaitObservable – no joy!

I’m going to drop in an agent, but I have to admit I’m a little stumpted! Going to keep looking!