News Tutorials Games Downloads Links Contact

Featured Author:
Tutorials - Blender Game Engine

Designing a Save and Load System

by blendenzo

Important Note: My usage of the pickle module in this tutorial is incorrect. I have not learned the correct usage yet, so I have not updated the tutorial.

Requirements: Some knowledge of Python
Example file: SaveLoad.blend (106kb)

PeterB on asked how one could make a save game feature for a Blender game using external save files, so I thought I'd write out a quick tutorial on the subject. As always, just click "Contact" on the menu bar to ask any further questions.

Saving Your Progress
If you want to save externally, you'll need a file to work with. Use the "open()" function in Python to access or create the save file. (If the file doesn't yet exist, calling it via open() will create it.) There's no need to give the file a common extension like ".txt". Give it a unique extension of your choice. For my example, I'll choose ".sav"

Let's get to working on the script. Your first line of code should read like this:

Python Code
saveGame = open("Quest1.sav", "w")

"saveGame" is the variable name for the file object in Python, and "Quest1.sav" is the filename of the save file. Since no path is specified, the file will be created in the same directory the game was run from. (NOTE: If you open Blender and load your game file, the game is running in the Blender directory. If you open the game directly by clicking the file, it is running in whichever directory the file is in.) The "w" in the line of code means "open the file for writing" (also, "r" means read, "r+" is read/write, "b" is binary for Win/Mac).

Now that the file is open, you need to add the relevant data to it. What should a save file include? It really depends on your game. Probably you will want to use things like player position on the map, score, inventory, and any progress indicators or player level numbers you may be using. It makes good sense to store whatever data you will be accessing in save/load operations as global variables, that is, properties of the GameLogic module (for example, "GameLogic.playerInventory[]"). In older versions of Blender, you need to import the GameLogic module at the beginning of any scripts that use it, but in newer versions it is inherently present so no import is necessary.

When you save the data, use the write() function. Here's an example:

Python Code

str() converts the variable "GameLogic.playerLevel" to a string for writing to a file, so really this code is saying "write a string version of GameLogic.playerPosition, then skip to a new line" ("\n" means new line). You can use int() on the variable when you load it later to convert it back to an integer.

If you're saving more complex data, like lists and matrices, you'll want to pickle it instead of using str(). I've not used the pickle module yet, so I'm not sure if you need to import it at the beginning of your code or not. Use this code to save the player position (a three digit list):

Python Code
saveGame.write(pickle.dumps(GameLogic.playerPostion) + "\n")

(Putting a new line after every variable will make the file easier to read back at loading.) This takes the player position variable and makes it a string ("dumps()" means dump to string, "dump()" will dump it to a specified file object). Later we can read it back using pickle.loads()

After you've saved all of the important data, close out the file to clear up the system resources you were using:

Python Code

Some Things to Consider Before Loading
You'll probably want to make sure that the file you're loading from is a real, existing file. If you try to load before the game is ever saved you could get undesired results. I would suggest using one of two methods to avoid loading when a game hasn't yet been saved.

First, you could just include a save file defining a default "start position". This way if the game has never been saved and they load from the save file, but the default will be overwritten at the first save. This works great for games like Mario 64, which has a certain number of "slots" for gameplay. Just define a default start file for each slot, then when a player uses the slot, save over it. You can include a reset function to reset the slot back to the default start position if you desire.

Alternately, you could create a "custom header" for valid save files. Just make the first line of each save file contain identical data. The string "1" (not integer, since Python can only read and write strings) or the sentence "This is a valid save file" are two examples of custom headers. Add the header at the start of every save process:

Python Code
saveFile = open("Quest1.sav", "w")
saveFile.write("This is a valid save file\n")
...other data here...

Then read the header at the start of every load process:

Python Code
loadFile = open("Quest1.sav", "r")
header = loadFile.readline()

## You will need to subtract the newline code
header = header[0:-1)]

if header == "This is a valid save file":
...load the rest of the file...
...notify the player that the load file is not a valid saved game...

Once you're sure you're loading a real saved game, load it in by reading the file with "readline()". Remember how I told you to use a new line at the end of every variable? Well, there was a reason. Now you can just call readline() once for each variable in the same order that you wrote them. Every time you call readline() it reads the next line in the file (the first time it reads the first line, the second time it reads the second line, etc...). You will need to subtract the newline character before you convert your variables, though. This can be accomplished easily using slice indices and the len() function. As in the example above:

Python Code
header = loadFile.readline()
header = header[0:-1)]

Here's some example code for loading a file containing the two variables that we were used earlier:

Python Code
loadFile = open("Quest1.sav", "r")

...If you have a header, read it first, then test...

level = readline()
GameLogic.playerLevel = int(level[0:-1)]

posi = readline()
GameLogic.playerPosition = pickle.loads(posi[0:-1)]

...Read in any more data you may have...


As you can see, it's simple to read the data back in. You just have to subtract the newline character and run the reverse of any conversion you did when saving, then assign that value to the appropriate variable. Always remember to close the file when you're done to free up precious system resources (especially when using a CPU intensive program like Blender).


Back to the tutorials index...

Website design by Tony "blendenzo" DiRienzo. All content Copyright Tony DiRienzo unless otherwise noted.