Thanks to the release of Prince of Persia, I haven’t been doing as much platformer hacking lately. Here are some bits and pieces, nonetheless:

I implemented loading and saving of games (though of course, I don’t have much to load/save right now – just the player’s position). A couple minor gotchas here, but otherwise it was quite easy:

  • I tried to use BinaryFormatter at first so I could just write arbitrary blobs to the saved games. It turns out that doesn’t work on the 360, so I had to fall back to using XmlSerializer - this means I need to know my type in advance, and I can’t store arbitrary types in my saves. I think in practice, it’ll work out okay, but this means I might need to write my own serializer if my saved games get big enough, since XML is pretty sluggish at times.
  • My first implementation of this had the nasty side effect of performing a load or save every frame as long as the respective key was held. Oddly enough, I didn’t actually notice this for a day or so – I’m using asynchronous IO in order to run well on the 360, so it happened to ‘just work’ despite the fact that it was slamming the hard disk. I only noticed when two Load operations happened to try and open the same save file at the same time, and got a sharing violation. Oops.
  • Even with the small amount of code I have, I already found one or two bugs revealed by loading and saving – it turned out that it was possible to break the game physics by loading during the upward part of a jump, because the game code assumed it was never possible to achieve upward velocity without having a jump in progress (loading breaks that rule). A clear reminder of the subtleties of game state if ever there was one…

For testing on the PC, I just mapped loading and saving of games to their own hotkeys. But on the 360, that isn’t really an option, so my next task was to implement a simple pause menu.

Right now, I’m just using a pretty simple implementation – I have a DrawableGameComponent that has a list of menu items to draw, and when it gets an appropriate button press, it invokes a handler for the selected item. I paired that up with a simple function that constructs a menu and then does the necessary magic to attach that menu’s item handlers to a Future, so that I can write simple tasks that yield on a menu, like so:

var f = ShowMenu("Hello", "World");
yield return f;

switch (f.Result as string) {

So far this approach is working pretty well. In order to pause the game when the menu is open, I had to add some pausing logic atop the default XNA IsActive logic. Instead of just using IsActive to determine whether or not to perform game updates and animation updates, I have a new property called Paused which is a combination of the existing IsActive property and a new internal _Paused field. I think this approach is too rigid, though, so in the future I’ll probably end up moving to a sort of state machine, and halting the game by putting a new ‘pause menu’ state in its place.

I also got fed up with how long it took to load the game (between loading the map XML and loading textures I was already having to wait up to 5 seconds for the game window to open), and moved all the content loading into a task so that it could make use of threads. This has the benefit of causing the game window to appear immediately, and allowing animation to continue while loading is in progress – I wrote a simple ProgressSpinner class to handle showing a tiny progress indicator onscreen whenever loading or saving is occurring, so you get a nice bit of feedback now while the game is loading, or while disk I/O is occurring (say, when you save your game from the pause menu). This is, in fact, another reason that I’ll probably want to move to a state machine – right now I have hand-coded logic to ensure that I don’t try and paint the map to the screen or update the player while the map is still being loaded from disk; a state machine model would allow me to just put map rendering and player updates in a MapView state and only run that state once all my I/O has completed.

Another I/O related note: I was suprised to discover that the first time I ran my Tiled map loader on the 360, it worked, but didn’t display any tiles. At first I thought I had botched the rendering code somehow, but in fact, the problem was much more subtle than I expected: the XBox 360 is a big endian machine, and Tiled stores tile data as base64 encoded little endian integers – so when I loaded a map on the 360, the tile values were bizarre, random integers, which my rendering code happily disregarded. Right now I’m using the unfortunate hack of calling Array.Reverse on every individual four-byte block of the decoded base64 data before I convert it to an integer – definitely not a long-term solution. I think this is just another reason to not use tiled’s native XML format for my maps (if I even use XML at all).

Since I don’t have any new screenshots to show off, here are some code snippets for your perusal:

        protected Future ShowMenu (params string[] items) {
            var f = new Future();

            var menu = new Menu(this);
            foreach (var item in items) {
                var menuItem = new Menu.Item {
                    Text = item,
                    Handler = (i) => {
                        f.SetResult(i, null);
                        menu.Close();
                        Paused = false;
                    }
                };
                menu.Items.Add(menuItem);
            }

            this.Components.Add(menu);
            Paused = true;

            return f;
        }
        protected IEnumerator<object> SaveGame () {
            var sg = new SavedGame {
                PlayerPosition = Player.Position,
                PlayerVelocity = Player.Velocity
            };

            yield return Storage.SaveToStorage("game.sav", sg);
        }
        protected IEnumerator<object> LoadGame () {
            var rtc = new RunToCompletion(
                Storage.LoadFromStorage<SavedGame>("game.sav")
            );
            yield return rtc;
            var sg = (SavedGame)rtc.Result;

            Player.Position = sg.PlayerPosition;
            Player.Velocity = sg.PlayerVelocity;
        }
        protected IEnumerator<object> ShowPauseMenu () {
            var f = ShowMenu(
                "Continue", "Save Game", "Load Game", "Quit"
            );
            yield return f;

            switch (f.Result as string) {
                case "Save Game":
                    _PendingAction = SaveGame;
                break;
                case "Load Game":
                    _PendingAction = LoadGame;
                break;
                case "Quit":
                    Exit();
                break;
            }
        }