Pumping the Guide

Originally posted to Shawn Hargreaves Blog on MSDN, Wednesday, June 10, 2009

I've never been happy with the design of the XNA Framework Guide methods.

I want to write code like this:

    int? button = Guide.ShowMessageBox("Save Game",
                                       "Do you want to save your progress?",
                                       new string[] { "OK", "Cancel" },
                                       0, MessageBoxIcon.None);

    if (button == 0)
    {
        StorageDevice storageDevice = Guide.ShowStorageDeviceSelector();

        if (storageDevice != null)
        {
            using (StorageContainer storageContainer = storageDevice.OpenContainer("foo"))
            {
                ...
            }
        }
    }

But there are no such simple ShowMessageBox or ShowStorageDeviceSelector methods. Even if these methods did exist, the above code would not work. Instead, I have to deal with a tangled mess of Guide.BeginShow*, IAsyncResult, and make a state machine to track when I should call Guide.EndShow*.

Why so complicated?

Back in Game Studio 1.0, we did have just such a simple storage device selector API. It worked fine as long as you called it from a background thread, but if anyone was so foolish as to call it from their main game thread, boom! The Xbox hangs.

People were justifiably surprised by this behavior. The problem is that the Guide UI is displayed over the top of the game, and relies on the game loop calling Present at regular intervals. When the game is blocked inside a ShowStorageDeviceSelector call, it is no longer cycling through the game loop, thus not calling Present, thus the Guide never gets a chance to render itself, thus the user has no way to interact with it, so the Guide call never completes.

It struck us as a bad idea for such a seemingly simple API to behave so rudely, so we spent some time trying to improve it for Game Studio 2.0. The main idea we considered was to make these blocking methods automatically call Present while the Guide was visible. Unfortunately, there are many problems with such a design:

When in doubt, play it safe and at least try to do no harm.

Game Studio 2.0 only provides async Begin/End versions of the message box, keyboard input, and storage device Guide calls. These are a pain to use, but at least they explicitly force developers to deal with the resulting state machine, rather than being surprised when crazy stuff happens and unexpected events are raised in the middle of a Guide call. No magic is better than confusing magic that only works half the time, right?

I'm still not happy about this. I keep revisiting it, looking at it from different angles, and concluding that it's still a mess. I don't like what we have now, but I also don't like any of the alternatives!

Blog index   -   Back to my homepage