I need to implement a save/load system, which I don’t have yet. In short, there are three times a game is saved or loaded: save during gameplay, save during menus, load during menus (load during gameplay is not valid). It’s more complex than that and I’ll get to that, but these are the key points (or rather transitions between these) where saving and loaded is needed.
There’s not much to save at the moment, but I do already have player’s inventory and upgrades. Firstly, I am making those as new systems that are responsible for storing player’s inventory and upgrades and passing them onto the in-game world mob when the game starts. These need to be stored separately from any world or player entities so they don’t get lost between runs, because the player doesn’t “exist” unless there is a world.
With that externalized, I can make my systems respond to save and load requests. I pass them a container where they can shove keyed values that they want and these will then be passed to some sort of save/load location, currently Unity’s internal player preferences. And after a whole lot of tweaking and fiddling, I have this glorious output:
This is my scrap and my upgrade-level data encoded into individual fields, split by system, with some meta information. I can easily extend this and add new save data — the systems wanting to load or save simply need to read or write their data to the save container, which gets processed independently.
And before I proceed any further, I will add a version number:
This is very important for any post-launch updates I will do where I cannot lose user’s data. I need every system to be backwards-compatible and be able to walk through previous version data and update it to new format, add missing, remove deprecated, or whatever actions it needs to do.
Now, I can call the loading at the start of the game and when the world starts, inventory and upgrade systems will tell the player mob who they are and the player mob can then use them normally, except their changes will actually get saved.
Now, there are four things that can happen in the level — the player wins, loses (dies), abandons (leaves), or crashes (hard-quits). None of these are straight-forward, so let’s go through and implement them one at a time.
If the player wins, the stuff that they gained or lost in the level (I’ll just used scrap as example) needs to be saved instantly:
This is pretty easy, since this is just the save logic from above. The interesting things happen if the player dies. I want to wipe any changes that happened to their stuff. In other words, I want to reload the game back to what it was at the start of the level once the player goes back to the menu:
And same for abandoning, if slightly trickier, since the player is still alive and we are leaving an active world instantly:
Now, I don’t really need special considerations for the game crashing mid-game just yet. I will have the progression stuff when I make level transitions and progress, but I’ll get to that in a second.
All of this is very finicky as there are so many moving parts. World, UI, game pipeline. Thankfully, everything has its own well-defined pipeline, so it’s a matter of carefully arranging the right events at the right times, like opening panels, saving/loading, changing game states, removing world, removing player, notifying things about world or player changes… And we’re not done yet.
There is a caveat that there is data I do want to save even when the player dies. For the proof of concept, I’ll implement a statistics system and add a “kill counter”:
When a player dies I need to roll back their scrap count, but not their kill count. In other words, I have to tell the system when it’s loading what is the reason for the load — actual save load or failed level reset. And this works out nicely:
So now I should add one of the more important systems — the level progression. I need to track that the player is or isn’t currently on a run. By default, they are not:
But, if they start a new game, they are on level 1:
This numbers gets increased in they win the level. It gets reset to no game if they lose. And it gets untouched if they abandon (my permadeath is optional, so I don’t really care if they cheese something they can just uncheck when starting a run).
Now, I don’t actually have any actual “levels”, that is, my world is my only world and there is no configurable option based on the player’s progression. So time to make a new asset to reference which levels to use for each number:
I also now need to add the accompanying continue button to the main menu:
And then fight some UI pipeline battles to get the whole UI pipeline working properly with loading screens and correct calls to stop and start the game properly.
Final addition is the “super-win” screen when the last level is reached:
And that pretty much establishes my whole game primary use case pipeline, minus polish and a bunch of usability. There are a couple critical confirmation windows I need to add. Firstly, asking the player if they want to overwrite their current progress if they click to start a new game:
And when they win, if they want to go back to main menu — I’d like them to continue:
I should also hide (may be disable later) the continue button when there is nothing to continue:
…which just looks like my old main menu.
And I think that about covers the main parts of saving and using this for level progression pipeline. A very important, often neglected part. But now I have this and it feels great for the “completeness” of the game.