Saving the Level Data and Reloading from a File


Now that we have the level score data stored in a dictionary structure, we will want to be able to save it in a file so that it will persist even after the player leaves the game, and then we can reload it once the player returns.

There are different ways to approach this task. One way might be to save a text file, and maybe write out a JSON string, which we can then parse back when we open the file and reload it to the game. Or we could skip the JSON and write and read the file line-by-line, but that could get a bit tedious and prone to errors. We could also store the data in an XML file. All of these are valid options.

Since we already have our score data stored in a dictionary object, wouldn't it be nice if we could save that object, and then reload reload the entire object itself, without having to manually put the data back into a dictionary piece by piece when the player returns? Well, with serialization, we can easily do that.

Serialization is a process of converting and saving objects as a stream of bytes, which can allow us to quickly store the game state to a file. Deserialization is the reverse process of converting those bytes back to an object.

Let's look at how we can serialize and deserialize our high scores dictionary example.

High Score Dictionary Example, Modified

Take a look at the code sample I posted at https://codepad.co/snippet/high-score-with-serialized-dictionary-example. Building on the example from the previous devlog entry, I demonstrate how you can use an external file to save and load score information. The comments in the code explain how it works.

First you want to make sure you have all the proper using directives at the top of the code:

using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary;

using System.IO;

You'll want the System.IO directive because we'll be working with outputting to and reading from files.

And then, write before the class name, add the serializable attribute:

[Serializable] class ScoreData

Note that to make the class fully serializable, all member data in the class should be serializable as well, so if your class has a custom class object as a member variable, make sure to add the serializable attribute to that one as well. Our dictionary is storing int values, which are already serializable.

I have added two methods that will handle serialization and deserialization: SaveData() and LoadData(). Notice that in both of them, we have the following line:

IFormatter formatter = new BinaryFormatter();

This object will handle the actual conversion to and from binary data format.

Notice also that both methods put the code to open and read from or write to a file in a try/catch block. If you're not familiar with that concept, it's basically a way to attempt to open and use a file, and if there's some sort of error relating to serialization or the file itself, the catch block will handle the error. In my catch blocks, I have code that outputs a message indicating that the data was not successfully saved or loaded; for your program you can decide on the best way to handle such an error.

To save the object, in the SaveData() method, we have these lines of code:

Stream stream = new FileStream("SaveData.txt", FileMode.Create, FileAccess.Write);

formatter.Serialize(stream, this);

stream.Close();

We are opening a stream to write out to a file called SaveData.txt (you can call your file whatever you want). Then we are using our formatter object to write the data to that file. Don't forget to close the stream after you're done writing the file!

And then to load, we do the reverse in LoadData():

Stream stream = new FileStream("SaveData.txt", FileMode.Open, FileAccess.Read);

ScoreData objnew = (ScoreData)formatter.Deserialize(stream);

highScores = objnew.highScores;

stream.Close();

Again, noticing that we are using a stream to open our file (but notice that the FileMode and FileAccess settings are different, because this time we are reading the file, rather than writing to it). Then, we recreate our ScoreData object by using the Deserialize() method. Notice that you have to explicitly cast the deserialized stream to be of type ScoreData (that's why we have ScoreData in parentheses before the deserialization). Again, don't forget to close the stream when the loading is completed.

So now you see how you can use serialization and deserialization to store and load your game's data.  Hopefully this approach helps you.  Please feel free to leave a comment or ask any questions!

Get Brain Bouncer

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.