Inertia

Gather ’round, kids, it’s time for yet another blog about serialization!

If it feels like I’ve been leaning hard on that topic in these blogs, that’s only because it’s proportionate to the amount of time and effort I’ve invested in these systems. Serialization in Gunmetal Arcadia is shaping up to be the crowning technical achievement of these games, in the same way the first implementation of the related entity-component system was in You Have to Win the Game and the editor and its XML-powered instantiation of entity components was in Super Win.

Recently, I’ve been tackling saved games in Gunmetal Arcadia Zero. I talked a little bit about the introduction of a three-slot save file system a la Zelda in last week’s video, but I haven’t really dug into how it works under the hood.

Last March (has it really been that long already?), I drew up this possibly-syntactically-accurate UML diagram.

entity_construction

And if you can follow that, it’s still pretty much the state of things. If not, it can be simplified to, “When entities are destroyed, they can write their current state into deltas in memory, and those can in turn be written to disk in turn. And then that whole process can be performed in reverse.” The part I’ve been focusing on recently is that link in the very bottom right, where the serializer meets the disk. In Super Win the Game, and in Gunmetal Arcadia Zero until just recently, this was a trivial process. The serializer would dump its content to a predetermined location on disk or replace its contents wholesale with contents loaded from that same location.

That was good enough for Super Win, but I’ve known for a while I’d need a more versatile solution for Gunmetal Arcadia and its eventual persistent state that exists beyond a single run, so it made sense to broach some of these topics for Zero. I’ve been imagining the interface for Gunmetal Arcadia would mirror The Binding of Isaac‘s three-slot system (which in turn was lifted from Zelda and countless other classic games), but that didn’t necessarily have to be the same for Zero. So I put it to a Twitter poll:

Aaand I guess I’m doing a three-slot system for Zero, too! That’s convenient. (In fact, I’m also tentatively supporting quicksave, barring any unresolvable issues it might raise.) So I threw together a quick prototype and an interface, and it looks like this.

threeslot

And then I had to solve all the problems this created.

The first thing to understand was that each of these save slots needs to encompass multiple saves on disk. One of these would be your last checkpointed location, the place you’d respawn on death. Another would be an immediate autosave location for exiting and resuming without losing data. Then there would be a manual quicksave that could be created at any point. These could all coexist without stomping over each other, but they’d all conceptually represent the same session, the same save slot.

So I broke these out into “profiles,” each of which could contain an arbitrary number of “saves.” (In this example, a “profile” is one of the three slots, and a “save” is a single snapshot of the game at any given time.) Zooming in on the corner of my old UML diagram, it would now look something like this:

more_uml

The implementation is about as straightforward as I can imagine. Each profile maps to a separate folder within a base saves location, each save maps to a separate folder within its respective profile, and inside that folder, I can put whatever data is associated with that save. In practice, this is currently a single file containing all serialized data. I had previously done some work to support saving and loading arbitrary binary data from additional external files, but a recent change to give each entity its own random number generator created too much file bloat as each one wrote a separate file to disk, so I’ve temporarily deprecated that path.

Where this got a little more interesting was in deciding how to handle death and game over states. I was starting with a few assumptions:

  • I want a finite number of lives in Gunmetal Arcadia Zero. My concern is that death becomes inconsequential with unlimited retries.
  • Running out of lives should not completely reset the game. That would be too punishing and fundamentally at odds with having a saved game.
  • The experience of running out of lives needs to be different in some way from the experience of dying once. That is to say, these can’t both simply send you back to the last checkpoint. There has to be something on the line to discourage failure.

This has led me to split the game’s checkpoints into two separate but occasionally overlapping concepts. There’s an initial checkpoint when you first enter a level, and then there may be additional midlevel checkpoints throughout. When you die, you return to the most recent checkpoint you reached, whatever that was. (Besides the start of the level, these will typically also occur immediately after boss fights.) When you run out of lives, you’ll get dumped back to the title screen, but you can choose to continue your game from the beginning of the last level you reached. I’m imagining this case will also incur some further costs, such as losing money or gear, but whatever the price, it won’t be a complete reset.

I’ve been racing to get these features online in time for the vertical slice coming up at the end of next week, and it’s just about there. I’ll still have a few game features left to implement after that, including figuring out how to transition from one level to another after boss fights (or from the final level to an ending sequence and credits), but I’m quickly reaching a point where I can focus almost entirely on content creation.