For the last few weeks I’ve been working on a game called A Place to Call. I’ll be blogging about its development later, but today I’m talking about how I got saving/loading into this particular game.
A Place to Call is a simple game where you break into an abandoned house, and set yourself up for the night by dragging objects around the house. The features I wanted for the serialisation of the game was the following:
- Each object that can move will revert to their starting position
- Each broken instances of an object are destroyed, or reverted to non broken
- All the games various states are reset to the beginning
So with this plan in mind, I set about trying to use serialisation in A Place to Call.
My first search took me to Unity’s Serializable Classes. My initial idea was to save each instance of the moveable objects into a binary file, so that when I reset the game world, I could delete everything in the map, then grab the instances of the objects back into the world. Unfortunately, classes that inherit from MonoBehaviour cannot be serialised in this way, meaning that none of the classes on the objects I wanted could be saved in this way.
So I decided to only have the important information: The objects position and rotation. I could simply move them back to their original place. So I decided to use a .csv file to store the information and I would manually grab the data.
To do this, the gameManager would have to keep track of every object
private static List move = new List();
To which each object would add themselves to this list on Awake. Saving out the data is simple, so we do this:
public void Save()
{
//delete the old save file
File.Delete(Application.dataPath + “/Resources/transform.csv”);foreach (GameObject go in move)
{//the save location path
File.AppendAllText(Application.dataPath + “/Resources/transform.csv”,
//save out the position
go.transform.position.x.ToString() + fieldsep + //fieldsep is a “,” char
go.transform.position.y.ToString() + fieldsep +
go.transform.position.z.ToString() + fieldsep +
//then the rotation as a quaternion
go.transform.rotation.x.ToString() + fieldsep +
go.transform.rotation.y.ToString() + fieldsep +
go.transform.rotation.z.ToString() + fieldsep +
go.transform.rotation.w.ToString() + linesep); //linesep is a “\n” char
}
}
This gives us a file that gives this data out for each moveable object, and ends the line after it.
7.948231,-1.119995,63.40053,0,-0.9452626,0,0.3263106
It’s all just a single line of numbers, but its easy enough to retrieve when we need to. Now we need to load the file. First, we have to have the file cached as a TextAsset from the Resources folder, but we can only do this after we make the file.
csvFile = Resources.Load(“transform”);
Then, we grab each line as an array of strings from the file:
string[] line = csvFile.text.Split(linesep);//whenever “\n” is hit, add a new element
Each element of the line variable now contains the position and rotation for an object. Next, we do a for loop to parse the information in each element if the lines
for (int index = 0; index < line.Length -1 ; ++index)
{
//separate the line into individual elements, using the comma
string[] commas = line[index].Split(fieldsep);
//then apply the position from each element
move[index].transform.position = //we know that index equals the obj we want because it saved in the same order as the list.new Vector3(float.Parse(commas[0]), //we know this is x, because of how we saved
float.Parse(commas[1]), //y
float.Parse(commas[2]));//z//then rotation as quaternion
move[index].transform.rotation =
new Quaternion(float.Parse(commas[3]), //x
float.Parse(commas[4]), //y
float.Parse(commas[5]), //z
float.Parse(commas[6])); //w
}
This ensures all objects are returned to their original starting position every time the function is called.
The rest is simply cleaning up the variables to ensure that when the game ends, the new game will be as if the game was started fresh.
As for the broken instances of a gameobject, I simply move broken objects into a different list in the game manager, and delete them when I need to. The regular moveable objects are no longer destroyed (such as when used as fuel for fire, or destroyed) they are moved off the map, where they can simply be moved again.
EDIT:
So it turns out that Unity doesn’t like you adding and removing files constantly when using Unity’s TextAssets functions. This resulted in Unity not remembering the correct order of objects, making them spawn in places they shouldn’t. This however, was an easy fix. Instead of using “Resources.Load(“transform”)” I make csvFile a string, which then loads ALL of the .csv file into one big line:
csvFile = File.ReadAllText(Application.dataPath + “/Resources/transform.csv”);
Then instead of csvFile.text.Split, I remove the “text” part and the serialisation continues as planned. Except this time, it isn’t “forgetting” which object it should be applying movement to, and csvFile is literally a massive ass line of text.
One thought on “Studio2: Serialisation in Unity”