How to script a Save/Load game option

I would like to be able to save and load a players progress to a players hard disk.

as an example if i have a player that collects 3 gems then a door will open automatically when the 3rd gem is collected. so id like to be able to save the location of the player (in the world or at a save location spot) as well as save the information that the crystals have been collected (i assume each crystal would have a unique name such as crystal_A crystal_B etc...so that when the player loads the game all the crystals won't be loaded again just the ones that have not been collected. )

I would like this system to work as a stand alone game (not a web browser run game) is it possible? does anyone know how?

OH and the reason for the hard disk save is so that if the player has to wipe the pc clean the player should be able to fine the directory where the file is saved and back up his save game. (this has saved me from loosing all my hard work on games i've played in the past when antivirus failed to protect my pc)

There are loads of solutions to that. The most used one is called PlayerPrefs and some documentation about that can be found in the docs. That approach does not save the data to a file which you can place anywhere you want though so that solution might be out of the question.

The second solution is called XML serialization and can automatically generate an XML file from a class, it can also put together the class from an XML class so you won't need to worry about writing your own parser. On the wiki there is a full example script using XML serialization.

The third option is using mono's serialization class, it works much like the XML serializer but requires a bit more work but is a lot more stable in return. I don't have any code here so I don't remember where the documentation is, a search on the forums always yields good results though.

The fourth option is to write a parser of your own, I have tried this and it is quite hard, especially when dealing with floats (vectors too). Documentation about this can be found when searching for XML text writer or StringWriter.

Hope this gives you some ideas! If you decide to use one option I can explain more about that then.

[Edit] Well I though a bit about the XML serialization, and actually I don't think it will be very good for your game to store it that way. The XML will look something like this

<Player>
  <Score score=245>
  <Life life=85>
</Player>

Which means the player will be able to open the XML file in a text editor and change whatever he/she wants, and that isn't so good, is it?

Stable wasn't the right word I think, limited would suite better, for example the XML serializer can't save Hashtables or any special stuff, neither can it save classes other than primitive types (I can't anyway, I have heard that it should work).

So I think the "ordinary" serializer would work better for you.

If you still want to use the XML serializer you can take a look here

The Serializer works like this:

You create a holder class for everything you want to save and then you mark it with the Serializable attribute:

[Serializable ()]
public class SaveData : ISerializable {
    public bool foundGem1 = false;
    public float score = 42;
    public int levelReached = 3;

    public SaveData () {
    }
}

Then you have to define functions for how the class should be loaded and how it should be saved, these should be put inside the class.

    public SaveData (SerializationInfo info, StreamingContext ctxt)
	{
		//Get the values from info and assign them to the appropriate properties
		foundGem1 = (bool)info.GetValue("foundGem1", typeof(bool));
		score = (float)info.GetValue("score", typeof(float));

		levelReached = (int)info.GetValue("levelReached", typeof(int));
	}

	//Serialization function.

	public void GetObjectData (SerializationInfo info, StreamingContext ctxt)
	{

	    info.AddValue("foundGem1", (foundGem1));
	    info.AddValue("score", score);
	    info.AddValue("levelReached", levelReached);
	}

The only thing left now is the save and load functions in your script.

public void Save () {

	SaveData data = new SaveData ();

	Stream stream = File.Open("MySavedGame.game", FileMode.Create);
	BinaryFormatter bformatter = new BinaryFormatter();
        bformatter.Binder = new VersionDeserializationBinder(); 
	Debug.Log ("Writing Information");
	bformatter.Serialize(stream, data);
	stream.Close();
}

public void Load () {

	SaveData data = new SaveData ();
	Stream stream = File.Open("MySavedGame.gamed", FileMode.Open);
	BinaryFormatter bformatter = new BinaryFormatter();
	bformatter.Binder = new VersionDeserializationBinder(); 
	Debug.Log ("Reading Data");
	data = (SaveData)bformatter.Deserialize(stream);
	stream.Close();
}

Oh, just one thing more, you need to define a VersionDeserializationBinder class, otherwise you can't load the game when you open it later, something about Unity generates a new key of some sort for the binary formatter, search for "Serialization" on the forum and you can find a post about that.

public sealed class VersionDeserializationBinder : SerializationBinder 
{ 
    public override Type BindToType( string assemblyName, string typeName )
    { 
        if ( !string.IsNullOrEmpty( assemblyName ) && !string.IsNullOrEmpty( typeName ) ) 
        { 
            Type typeToDeserialize = null; 

            assemblyName = Assembly.GetExecutingAssembly().FullName; 

            // The following line of code returns the type. 
            typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) ); 

            return typeToDeserialize; 
        } 

        return null; 
    } 
}

You will need to use these namespaces to get it working:

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

using System;
using System.Runtime.Serialization;
using System.Reflection;

Done, at last :D

I took TowerOfBricks's code for data serialization and put it together into a single script, complete with thorough commenting. Might be good for people looking to understand the save/load process a little better.

using UnityEngine;    // For Debug.Log, etc.

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

using System;
using System.Runtime.Serialization;
using System.Reflection;

// === This is the info container class ===
[Serializable ()]
public class SaveData : ISerializable {

  // === Values ===
  // Edit these during gameplay
  public bool foundGem1 = false;
  public float score = 42;
  public int levelReached = 3;
  // === /Values ===

  // The default constructor. Included for when we call it during Save() and Load()
  public SaveData () {}

  // This constructor is called automatically by the parent class, ISerializable
  // We get to custom-implement the serialization process here
  public SaveData (SerializationInfo info, StreamingContext ctxt)
  {
    // Get the values from info and assign them to the appropriate properties. Make sure to cast each variable.
    // Do this for each var defined in the Values section above
    foundGem1 = (bool)info.GetValue("foundGem1", typeof(bool));
    score = (float)info.GetValue("score", typeof(float));

    levelReached = (int)info.GetValue("levelReached", typeof(int));
  }

  // Required by the ISerializable class to be properly serialized. This is called automatically
  public void GetObjectData (SerializationInfo info, StreamingContext ctxt)
  {
    // Repeat this for each var defined in the Values section
    info.AddValue("foundGem1", (foundGem1));
    info.AddValue("score", score);
    info.AddValue("levelReached", levelReached);
  }
}

// === This is the class that will be accessed from scripts ===
public class SaveLoad {

  public static string currentFilePath = "SaveData.cjc";    // Edit this for different save files

  // Call this to write data
  public static void Save ()  // Overloaded
  {
    Save (currentFilePath);
  }
  public static void Save (string filePath)
  {
    SaveData data = new SaveData ();

    Stream stream = File.Open(filePath, FileMode.Create);
    BinaryFormatter bformatter = new BinaryFormatter();
    bformatter.Binder = new VersionDeserializationBinder(); 
    bformatter.Serialize(stream, data);
    stream.Close();
  }

  // Call this to load from a file into "data"
  public static void Load ()  { Load(currentFilePath);  }   // Overloaded
  public static void Load (string filePath) 
  {
    SaveData data = new SaveData ();
    Stream stream = File.Open(filePath, FileMode.Open);
    BinaryFormatter bformatter = new BinaryFormatter();
    bformatter.Binder = new VersionDeserializationBinder(); 
    data = (SaveData)bformatter.Deserialize(stream);
    stream.Close();

    // Now use "data" to access your Values
  }

}

// === This is required to guarantee a fixed serialization assembly name, which Unity likes to randomize on each compile
// Do not change this
public sealed class VersionDeserializationBinder : SerializationBinder 
{ 
    public override Type BindToType( string assemblyName, string typeName )
    { 
        if ( !string.IsNullOrEmpty( assemblyName ) && !string.IsNullOrEmpty( typeName ) ) 
        { 
            Type typeToDeserialize = null; 

            assemblyName = Assembly.GetExecutingAssembly().FullName; 

            // The following line of code returns the type. 
            typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) ); 

            return typeToDeserialize; 
        } 

        return null; 
    } 
}

Another option is to purchase a third-party save/restore tool, such as EZ Game Saver from Above & Beyond Software.

@The Stone: Big THX for sharing, this serialization works perfectly.

I've tested it on the iPad with Unity 3. You can use it the exact way, except you have to choose carefully where to save and load the file, because you only have restricted access to the device's folders.

For me the Application's "Documents" folder worked very well:

`Stream stream = File.Create(Application.dataPath + "/../../Documents/" + "MySavedGame.game");`

Use the same path for loading.

Maybe another option would be to serialize to XML then zip up or encrypt the XML in some way and save it out to disk.

Based on CJCurrie’s answer, I modified it and made the class entirely dynamic.
First off, I added a static SaveData member in the SaveLoad class like this:

public class SaveLoad
{
    public static SaveData data = new SaveData();
    [...]

I then proceeded to modify the Save and Load in functions in SaveLoad to work towards this new variable (rather than towards a SaveData variable created directly in the functions).

Furthermore, in the SaveData class, I modified GetObjectData(…) and the SaveData(…) functions so that they look like this:

    public SaveData(SerializationInfo info, StreamingContext ctxt)
    {
        FieldInfo[] fields = this.GetType().GetFields();
        foreach (FieldInfo f in fields)
        {
            FieldInfo fi = this.GetType().GetField(f.Name);

            if (fi == null)
                Debug.Log("Field is null! Property name: " + f.Name + " --- Type: " + f.FieldType);

            fi.SetValue(this, info.GetValue(f.Name, f.FieldType));
        }
    }

    public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
    {
        FieldInfo[] fields = this.GetType().GetFields();
        foreach (FieldInfo f in fields)
        {
            info.AddValue(f.Name, f.GetValue(this));
        }
    }

This way, every PUBLIC (<-- this is important) variable you add to the SaveData class will get both loaded and saved automatically when you call the Save and Load methods in the SaveLoad class.

This post has an excellent solution for those of you that would rather use JavaScript:

http://forum.unity3d.com/threads/94997-Need-Help-Save-Load

I explain the entire process all the way down to modifying individual variables such as incrementing the “levelsCompleted” by 1 and more!

You could also save the xml file and save a generated crc in a separate file. Then when you load the file simply check the crc saved with a newly generated crc from the save data. This will tell you if it has changed and so you can tell the user to man up and not cheat! :P

You'll have to save the crc in a separate file as the act of saving it in the save data itself will change the crc.

I’m having some troubles with this and will be discussing it further here:

http://forum.unity3d.com/threads/94997-Need-Help-Save-Load

another way is using JSON and txt files. saving data is where the fun is at.

Oh, can anybody help me in a similar situation? I want my game to share saves between all the users in OS (I want allow all the users to see all the game profiles). Is there any way in Unity to get a folder where I could store save files in? (for example, in Windows this folder would be "Documents and Settings\All Users\Application Data" or in MacOS “/Users/Shared/”)

I have released my generic Save/Load game code on 100% free public MIT license. Available here: http://whydoidoit.com/unity-serializer-v0-2/

UnitySerializer is a full level serialisation plug in. It enables you to save the progress of a game and restore it at some point in the future. It does this with minimal impact on the design of your game and requires minimal modification to your behaviours and classes.

UnitySerializer has been tested working on iOS and should work fine on Droid.

The serializer supports storing newly instantiated prefabs, as well as the status of existing scene objects (such as those that might be destroyed during the course of a game).

UnitySerializer supports the serialization of your custom classes and properties as well as standard Unity components

  • Animation support
  • Rigidbody support
  • Support for NavMeshAgent (although the agent must recalculate its path on restoration)
  • Transform – including parent/child relationships
  • Important: UnitySerializer cannot store the current state of an iTween at present, due to the way in which iTween works. If you need path following you can use the code found elsewhere on this blog which will resume fine.

In addition, presuming you use my extended coroutine start functions, all of your coroutines will continue executing from the point that they left off and will be restored with all local variables intact. You need make no changes to your coroutines – just to the way that you start them.

The status of your level is stored in a string which can be placed in PlayerPrefs – stored on a server or in a file.

I gonna probably dig up this topic but I’ve got a question.

I want do the same thing with my iPhone program to save data but when I do that, I got this on Execution :

EXC_BAD_ACCESS in the function :

public sealed class VersionDeserializationBinder : SerializationBinder

If someone got an idea or want more details on this…

Note: I’m the author of this library.

I struggled a lot with object serialization on Unity, so I created an lightweight class to deal with this. Easy Serializer is a one-file project that provides one-line serialization and deserialization compatible with standalones, iOS and Android. Creds to @TowerOfBricks from this thread from who proved some bolierplate code.

Unity’s official answer:

http://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/persistence-data-saving-loading

It depends on your need, if you want to save data in the player storage, you need to use File I/O, if you want to save data to the cloud and sync it across user devices you will need a database and server.

There are some third-party cloud solution like Firebase and PlayFab

And for File I/O and serialization you can use third-party or built-in tools, for example, you can use BinaryFormatter for serialization, or you can use a JSON serialization library.

If you need Cloud storage, i recommend you to use PlayFab for Cloud storage on Standalone platforms and such, and Firebase for Cloud storage on Mobile platforms.

Otherwise, if you needed File storage, you need to implement it from scratch or use a third-party solution, but it is waste of time to implement one from scratch instead of using an available tool.

Such useful free tool is Save Game Free that is free and open source and lets you save data to the user storage and cloud storage.

But you can use more powerful and complete solution, for example, Save Game Pro that lets you save everything, everywhere that also lets you save and load from Web & Cloud.

Also, Save Game Pro provides integration for Firebase, PlayFab, PHP, Node.js for cloud storage and provides PlayMaker integration for visual scripting. More integrations are coming.

I made a script for saving and loading any type of data
https://github.com/jktarban/Unity-Save-Load-Data