Save & Load - BinaryFormatter
Saving and Loading Data using BinaryFormatter in Unity
Here, we'll learn how to save and load data using Binary Formatter in Unity.
What's great about Binary Formatter for saving data is that execution is fast and it uses little storage space. Also, the file is not human-readable so it cannot be tampered with as easily as a JSON file for example. It's still not 100% secure as there are many software that can process the file to make it human-readable, so you may want to find other solutions if you really want to prevent data tampering. For now, let's jump right in and learn the basics.
(Not in the mood for a step-by-step guide? Here's a GitHub repository for ya!)
First, we need to add these to the top of the script to be able to use FileStream and BinaryFormatter.
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
Now, let's go through our initialization setup and create a class with all the data to be saved. We'll need to add a System.Serializable attribute on it so that the BinaryFormatter can use it. For example:
[System.Serializable]
public class PlayerData
{
public int health;
public int gold;
public float xPos;
public float yPos;
public float zPos;
}
Note that the position is captured separately for each axis rather than using Vector3. Currently (the day of the creation of this tutorial), Unity's serialization of Vector3 is different than .NET's Binary Formatter making them incompatible. Separating the Vector3 into individual floats bypasses this issue.
We'll continue by creating a new object for the data and adding the values to your liking. For example:
PlayerData playerData = new PlayerData();
playerData.health = 100;
playerData.gold = 5;
playerData.position = Vector3.zero;
We also need to create a Binary Formatter object to serialize and deserialize our data.
BinaryFormatter binaryFormatter = new BinaryFormatter();
And finally, before we save and load data, we need a location for the file to be stored.
string saveFilePath = Application.persistentDataPath + "/PlayerData.dat";
The data are all prepared and ready to be saved. The code, at the moment, may look like this.
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
[System.Serializable]
public class PlayerData
{
public int health;
public int gold;
public float xPos;
public float yPos;
public float zPos;
}
public class SaveLoadBinaryFormatter : MonoBehaviour
{
PlayerData playerData;
string saveFilePath;
void Start()
{
playerData = new PlayerData();
playerData.health = 100;
playerData.gold = 5;
playerData.xPos = 0;
playerData.yPos = 0;
playerData.zPos = 0;
saveFilePath = Application.persistentDataPath + "/PlayerData.dat";
}
}
Next, we save these data to a file. Start by creating a file to stream our data into.
FileStream file = File.Create(saveFilePath);
Serialize the data using the Binary Formatter to the stream.
binaryFormatter.Serialize(file, playerData);
Finally, go ahead and close the stream.
file.Close();
We've successfully saved our data to a file! Our save method looks like this.
public void SaveGame()
{
FileStream file = File.Create(saveFilePath);
binaryFormatter.Serialize(file, playerData);
file.Close();
}
Now we want to have the ability to load our data from the file we saved. This works similarly to the save method. Start by opening a FileStream to our saved file.
FileStream file = File.Open(saveFilePath, FileMode.Open);
Deserialize the data from the file into our data object.
playerData = (PlayerData)binaryFormatter.Deserialize(file);
And don't forget to close the stream.
file.Close();
Loading data is done! Here are some useful things to manage your data saving and loading. This one is to check if a file exists, which will return a bool value.
File.Exists(saveFilePath)
And for file deletion, use this.
File.Delete(saveFilePath)
The code for loading and deleting the saved data may look like this.
public void LoadGame()
{
if (File.Exists(saveFilePath))
{
FileStream file = File.Open(saveFilePath, FileMode.Open);
playerData = (PlayerData)binaryFormatter.Deserialize(file);
file.Close();
}
}
public void DeleteSaveFile()
{
if (File.Exists(saveFilePath))
{
File.Delete(saveFilePath);
}
}
Time to combine everything we learned! The final saving and loading script may look like this. Inputs and debugging tools were added so you can dive in to test it out in Unity!
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
[System.Serializable]
public class PlayerData
{
public int health;
public int gold;
public float xPos;
public float yPos;
public float zPos;
}
public class SaveLoadBinaryFormatter : MonoBehaviour
{
PlayerData playerData;
BinaryFormatter binaryFormatter;
string saveFilePath;
void Start()
{
playerData = new PlayerData();
binaryFormatter = new BinaryFormatter();
saveFilePath = Application.persistentDataPath + "/PlayerData.dat";
playerData.health = 100;
playerData.gold = 5;
playerData.xPos = 0;
playerData.yPos = 0;
playerData.zPos = 0;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.S))
SaveGame();
if (Input.GetKeyDown(KeyCode.L))
LoadGame();
if (Input.GetKeyDown(KeyCode.D))
DeleteSaveFile();
}
public void SaveGame()
{
FileStream file = File.Create(saveFilePath);
binaryFormatter.Serialize(file, playerData);
file.Close();
Debug.Log("Save file created at: " + saveFilePath);
}
public void LoadGame()
{
if (File.Exists(saveFilePath))
{
FileStream file = File.Open(saveFilePath, FileMode.Open);
playerData = (PlayerData)binaryFormatter.Deserialize(file);
file.Close();
Debug.Log("Load game complete! \nPlayer health: " + playerData.health + ", Player gold: " + playerData.gold + ", Player Position: (x = " + playerData.xPos + ", y = " + playerData.yPos + ", z = " + playerData.zPos + ")");
}
else
Debug.Log("There is no save files to load!");
}
public void DeleteSaveFile()
{
if (File.Exists(saveFilePath))
{
File.Delete(saveFilePath);
Debug.Log("Save file deleted!");
}
else
Debug.Log("There is nothing to delete!");
}
}
Aaand we're done! Now you can add more features like save slots, autosaves, and more! Check out this GitHub repository to see the example we created for this topic. Feel free to download the script and test it out yourself. Have fun!
Level up your game development by finding more stuff to learn in our growing list of pixel-sized tutorials.