In this Coffee-Break tutorial, we're going to look at creating a checkpoint in a game using buffers. However, before getting started, let's quickly explain what a buffer is and how it works, as even veteran users can sometimes be put off by the idea of using them and see them as being something complicated (they're not!).
NOTE: This tutorial is for people that use Drag and Drop. If you prefer to use GML to make your games, we have a companion article for you here.
Basically, a buffer is a space in memory that you tell the device running the game to "set aside" as a storage area for information. Simply put, you create a buffer and give it a size (the bytes of memory that it will require) then you write the data you want to store to it. Once filled, the buffer can be saved to a file and loaded again, and you can read the data back from it. In general, data is written to - and read from - a buffer in consecutive order, from the start of the buffer to the end, which makes it ideal for storing chunks of information that will be used all in one go, like a save game or a checkpoint!
Sounds simple enough, doesn't it? Now, there are nuances to using buffers and they can actually do a lot more than we've outlined above, but for the purposes of our tutorial today, that's really all you need to know to get started using them, and if you want to know a bit more of the in-depth details of how they work then you can check out the following section in the manual: Using Buffers.
To start with, you will need to download and import into GMS2 the following YYZ file, as it will provide the base on which we are going to build our checkpoint system:
If you run the project, you'll see that this is a simple brick-breaker game controlled by the mouse, and it has 3 levels that can be played.
At the moment, if you die or leave the game on level 2 or 3 then you have to go back to level 1 and start again. What we're going to do is create a checkpoint that saves the game state information at the end of each room so that you can load the game from that point again, and not lose your progress. For that we'll need a new object, so go ahead and create a new object now and call it "oSaveControl". We want this object to be persistent so make sure to check that flag in the object properties:
Making this object persistent means we can add it into the first room of the game and it will be carried over to the rest of them when rooms change, making it ideal for storing information. With that done, we can start to add our Drag And Drop™...
ROOM END EVENT
We're going to start by adding a Room End event (from the Other event category). We'll be using this event to check which room we are in, and if it is not the title room (ie: it is a game level room), then we will save the data for the room so that it can be used as a checkpoint. We also need to check the number of balls (lives) that the player has to make sure we don't save when the room ends because of a Game Over.
So, let's add those checks to start with:
We then need to create the buffer and write the important data to it. In this case we want to save the number of balls the player has, the current score, and then the room that the player has just finished. Let's first create the buffer and save the player balls and score:
Here we create the buffer, and the action stores the buffer ID in a variable. We then use this variable in the Write Buffer actions to target the buffer and write the score and balls to it. For saving the room, we'll need to use a Switch, as you shouldn't try and save room IDs directly since they can change and are considered "internal" values by GameMaker. Instead we'll save out a simple number to signify the room, like this (which goes directly after the the last Buffer Write action):
You'll notice here that for Level 1 we are saving 2 as the value, for Level 2 we save 3, etc... This is because we've completed the current level at this point, so we will want the load system to know that the room the player should go to when we load is the next one.
The final thing to do here is save the buffer to a file, then delete it, as we don't need to keep it around in memory. For this we'll add the following actions after the Switch:
That's the actions we need for saving, so let's now add the ones we need to load the game. These will go in the Step Event of our save controller object, so add that event now. We only want to load data if the player is in the main title room, and we also only want to permit the player to load a game if a save file actually exists, so we'll be starting with those checks. We currently use the left mouse button to start the game, so we'll keep using that for a new game and use the right mouse button to load the game. This means we also need to check those buttons. Put that all together, and you should add the following DnD™ into the Step event:
You'll notice that the left mouse button action will delete the save file file and reset the global variables for balls and score. This ensues that when a new game started, the save file created will be for the that game and that the data is saved correctly.
Now we need to add the actions that will load our checkpoint data from the saved buffer file. Remember, the data in a buffer is written and read in consecutive order, so if we wrote the number of balls, then the score and then the room into the buffer when we saved it, that is the exact order in which we need to read it back. With that in mind, we can add the following DnD™ to get the values and store them in temporary local variables, then delete the buffer as it's no longer required (these actions go in the right mouse button action, so they are only performed if it is pressed):
It is important to note that regardless of the values that were written to the buffer, when you read them back they will always be strings. This is a minor limitation on the way Drag and Drop™ deals with buffers and is not something that is present using GML. However, it's not much of an issue as we can easily convert the strings back into real numbers, as you'll see in the following set of actions.
To complete our load code in the Step Event, we need to set the player variables and then go to the appropriate room, so we'll do that with the following DnD™, added after the Delete Buffer action:
As you can see, we convert the balls and score data from the buffer into real numbers and assign them to the appropriate global variables, before checking the room value and sending the player off to the correct room.
DRAW GUI EVENT
We're almost ready to test things, but before we do we need to let the player know that they can load a saved game from the title screen. For that we'll add some very basic DnD™ into the Draw GUI Event so that it shows on the title screen if a save file is present. Add this event to the save controller object now and give it the following DnD™:
With that done, all that's left is to add this object into the room "rTitleScreen", so open that room now and then select the layer "Instances". Once selected, drag the object "oSaveControl" onto the layer (anywhere, although I usually put controllers in the top left corner).
You can test the game now and see if the checkpoint save system is working. Simply play the game and pass the first level, then deliberately lose the second level so you are taken back to the title screen. On the title screen you should now see the text "Press RMB to load game", and if you do that then you should start the game on level 2 and with the same score and lives you had when you finished level 1.
Hopefully that all works and you have now got a better idea of how easy it is to use buffers for things like this. You could go ahead and expand on this example now by adding in more levels (in which case you'd need to expand the "Switch" actions for loading and saving), or adding in power-ups that are carried across levels (in which case you'd need to add further information into the buffer then read it back to generate any effects on load). As long as you are clear on the following important points you shouldn't have any issues using buffers in this project or in your own projects:
Buffers require memory and should be deleted when no longer required
You should always read data from a buffer in the same order that it was written to the buffer
When using DnD™ all data written to a buffer will be converted into a string
That's it for another Coffee-break Tutorial! Happy GameMaking!