Building an adventure game save system in Unity.

Today I’d like to talk about the save system I designed for Bik.

I’ll try to be as simple as possible so you can use this information in any way you need.  Originally I began to implement my own method and would save the information to XML files but it was taking a while and was not very robust, and I wanted the game to come out before the end of my life, so I turned to the Asset store once again.  I use a plugin called Easy Save 2 for actually writing and reading the variables to and from a file.

So writing the variables to a file is no longer the hard part.  The hard part is finding a good and efficient way of actually collecting and handling the saving. Enter C# delegates and events.

Here is an amazing video tutorial on using Events and Delegates in Unity.

Basically, I have a master save script that handles the saving and loading.  If a script wants to save a variable it must subscribe to the save or load event on the master save class and then when it is time to save or load, the master script will tell its subscribers to save or load.    I built in a 3 level ordering system in case certain objects should load before others.
My actual save script is a lot more complex but hopefully these distilled points will point you in the right direction!

 

        //First setup the delegates and events
 	public delegate void SaveType();
	public static event SaveType SaveEarly;
	public static event SaveType SaveLate;
	public static event SaveType SaveRealLate;

	public delegate void LoadType();
	public static event LoadType LoadEarly;
	public static event LoadType LoadLate;
	public static event LoadType LoadRealLate;

//If we want to save or load we can call these methods and it will fire off the events in the order they are placed below.  Check for null otherwise we get an error if there are no subscribers.

//this is my actual save method I currently use.  
public void Save()
{
       //check if the file exists and delete it because its going to get created again
		if(ES2.Exists(saveFile)) ES2.Delete(saveFile);

		//Global Variables Holder.  This save any global variables that need to persist between scenes
		GlobalVariablesHolder.Instance.SaveGlobalVars();

		//Save current Scene State
		AO.controlcenter.GetComponent().SaveSceneState();

		//Registered Objects
		if (SaveEarly != null) SaveEarly();
		if (SaveLate != null) SaveLate();
		if (SaveRealLate != null) SaveRealLate();

		//Current Level
		ES2.Save(Application.loadedLevelName, saveFile+"?tag=CurrentLevel");

		//Inventory
		ES2.Save(AO.inventory.GetComponent().inventory, saveFile+"?tag=inventory");

		//Timer
		ES2.Save(timer, saveFile+"?tag=timer");

		//MouseTarget
		ES2.Save(AO.mouseTarget.transform.position, saveFile+"?tag=mouseTarget");

		Debug.Log("saved");	

}

//loading is a lot more complicated so I'll just show you a few examples
IEnumerator Load()
{

        //current scene state - this happens AFTER we load correct scene.  We now will load the state that the scene was in when saved
	SceneStatesController statesCon = AO.controlcenter.GetComponent();
	statesCon.RestoreSceneState();

	//we now yield the script while the state is loaded.
	while(SceneStatesController.stateLoading) yield return null; //wait for state to load to continue

	//now load any specific variables to override current scene state

        //Global Variables holder
	GlobalVariablesHolder.Instance.RestoreGlobalVars();

        //load the mouse target
        AO.mouseTarget.transform.position = ES2.Load(saveFile+"?tag=mouseTarget");	

        //fire off load events for specific items and objects
        if (LoadEarly != null) LoadEarly();
	if (LoadLate != null) LoadLate();
	if (LoadRealLate != null) LoadRealLate();
}

and here is what the part of a script would look like that is subscribed to the saving or loading events.

//This is used to subscribe to the event.  We put it in the OnEnable and OnDisable so it does not subscribe unless the object is active and if it is ever destroyed or deactivated it will unsubscribe itself.
 void OnEnable(){
		if (!lateRestore){
                         //the "Save" after += is the LOCAL method that will be called whenever the SaveEarly event is fired on the SaveSystemV1 class
			SaveSystemV1.SaveEarly += Save;
			SaveSystemV1.LoadEarly += Restore;
		}
		else {
			SaveSystemV1.SaveLate += Save;
			SaveSystemV1.LoadLate += Restore;
		}
	}

	void OnDisable(){
		if(!lateRestore){
			SaveSystemV1.SaveEarly -= Save;	
			SaveSystemV1.LoadEarly -= Restore;
		}
		else {
			SaveSystemV1.SaveLate -= Save;
			SaveSystemV1.LoadLate -= Restore;
		}
	}

        void Save()
        {
//say I want to save if a collider is on or off I can use this
ES2.Save(collider.enabled, SaveSystemV1.Instance.saveFile+"?tag="+gameObject.name+"collider");
        }

        void Load()
        {
//and to load if the collider was on or off I use this
collider.enabled = ES2.Load(SaveSystemV1.Instance.saveFile+"?tag="+gameObject.name+"collider");
        }

And that is the basic idea for the save system. It incorporates nicely with my state system for level states and I can expand it with ease to work with any object in the game I need to simply by dragging a generic save script I created! Any questions or if you want more specifics please feel free to leave a comment!

This entry was posted in DevBlog. Bookmark the permalink.

5 Responses to Building an adventure game save system in Unity.

  1. spider says:

    Hi.

    I’m using JavaScript, (UnityScript) in Unity and am interested in how you’d approach saving/loading if you weren’t using C#? I appreciate you are using C#, but hypothetically, how would you go about it? Great blog by the way, the post on how you did your inventory system was really useful.

    Thanks

  2. Mike Pinto says:

    Hi spider,
    Thanks for checking out the blog.
    The general idea is exactly the same between UnityScript and C#. You have a static save controller and then save scripts that attach to gameobjects. The objects should listen for a save event and then report their information to the save controller for saving. If UnityScript does not have events, then you could have each script register itself on the Start method with the save controller. The save controller would go through an array of all the registered save objects and tell them when it is saving. Same goes for loading.

    As for saving and retrieving the actual data to disk, you might want to check out how to use System.XML to create a stream, and write the stream once all the objects are done adding to it, or a much simpler solution would be to check out one of the save plugins on the asset store.

    Hope that helps!

  3. Hamit says:

    Hello, thanks for your educating blog,
    I have two scenes in my games. one of them is main menu and other one is level 1.

    In the main menu, i have Load Button to open ‘Level 1’ scene saved before.
    In level 1, i have ‘Pause Button’ when i press Pause Button, Gamer can see some of buttons related to some options. I have here ‘Save Button’ wanting save all of scene when gamer wanna press here. and when i wanna press ‘Load Button’ in the main menu, i wanna load this saved scene. Can you help me how to fixed my problem here. Have a nice day.

    • Mike Pinto says:

      Hi Hamit, what you ask for is complicated and very specific to your game and what you want to save/load. I suggest doing some searches for saving and loading data in Unity. Short story is you can use xml, json, or a text file…and then read the data from that file and do what you need with it.

      • Hamit says:

        Hi Mike, Thanks for answer. maybe i couldnt explain well. i already searched but there is nothing i found for my game. I just wanna open scene which is saved before- not data- from main menu and gamer can continue game the place where he stayed in the game. I tried to use PlayerPrefs methods but i couldnt.

Leave a Reply

Your email address will not be published. Required fields are marked *