2015/07/12

Content Management: Save Game Serialization

As promised, I looked into Xna.Framework.Storage, and now I know why no one ever seems to bother with it: The API is messy, it revolves around Gamer Services (which aren't exactly supported on Windows) and the XBox 360 UI. Also, it still leaves the serialization up to you, so we'll just do that and build our own, more lightweight API around it.

Save Game Data

Before we start serializing stuff, we need to consider what we need to save. I can safely say now that this game will try to be to 16 bit RPGs as Shovel Knight was to 8 bit platformers, so let's turn to those for help.
We will need to store the player's position in the world, the state of the party and their inventory. Until the map, combat and inventory systems are coded, however, we don't know how they should be stored specifically. More importantly, anyway, is story progression, and since I already have a pretty good idea how the scripting system should work, I can already tell that we will need, at minimum, an array of ints, used as persistent variables in scripts. I'll also add an array of bools, serving as simple flags, to keep track of which events have been triggered already.

To serialize our data, we then need to apply the [Serializable] attribute to our class. But we're not quiet done: When saving the game in the background, i.e. in another thread, we might want to change it in the main thread, so we'll clone our SaveGameData before writing it.
At this point, the class looks like this. Nothing spectacularly ingenious, but this is just the data representation. Not that the serialization itself will be spectacularly ingenious either.

Save File Manager

Object Serialization in .NET is a well documented, exceedingly simple process: You'll need a formatter, a stream, and a [Serializable] class; open the stream, use the formatter to (de)serialize and close the stream. You can keep the formatter around for later use.

The problem is, that all three steps are potential failure points. Luckily for us - sort of -, there aren't very many recovery options: essentially, all of them amount to: tell the player that the read/write could not be completed.
However, we don't have the facilities to do that available in our SaveGameData class, nor should we make them so: Informing the user of the failure should be the job of the UI, so we won't bother catching any exceptions in our serialization code.
Still, we'll want to log the reading and writing process, so we can get an idea of what caused the failure. To that end, we make use of the TracerCollection again. Since I don't want to chuck around a TraceSource when cloning the SaveGameData, and the formatter and file streams shouldn't concern the SaveGameData, I'll make a class SaveFileManager that does all that for me.

The source code for that class can be found here. You'll notice that this class doesn't concern itself with threading. This is because I will probably write a job system for fire-and-forget operations like this, as well as purely functional data crunching (e.g. path finding); I've already tested it in that kind of context, and didn't find any modifications necessary.

No comments:

Post a Comment