Programming
Article

Saving Data Between Scenes in Unity

By Zdravko Jakupec

This tutorial assumes basic knowledge of Unity Engine. If you don’t have your own project set up, you can freely grab the project example linked here. You will also find a download of the completed project at the end of this article.

If you’re struggling with saving data between two scenes, this is the tutorial for you.

unity-logo


Starting Point

Download the example project:

[GitHub Repository link]
[ZIP Download]

The logic

Unity is a game engine with its own philosophy. Even though there are quite a few alternatives, it’s quite unique in the way it handles the building blocks of any game – game objects, scenes, code, scene graph. And by unique, I mean how easy it is to understand it.

If you’ve tried giving Unity a test run because it’s free to download, you were probably introduced to how its scripting is implemented. „Scripts“, written in either C#, JavaScript (or, since Unity 5, UnityScript) or Boo language, are components that are attached to any game object. From within a script, you’re free to access and manipulate the object the script is attached to, or any linked objects. It’s quite intuitive, easy to use, and fun to build with.

Supposedly, then you tried building a second level; say your first level was the interior of the house, and then your second level is outside of the house. The transition is using the door to load the next scene.

Here’s the core problem we’ll be tackling today. Each level within the Unity engine is called a „Scene“. You can edit the Scene using the graphical editor however you like. You can transition the scene using a single line of code (that is triggered, perhaps, by the player touching the door, or using an object, etc). Each scene has objects, which have „components“.

1

Generic „Objects“ represent what ever else makes up your level (meshes, triggers, particles, etc)

Each scene is built in its initial state. Transitioning the scene to another one means the new scene will be loaded at its initial state (naturally). But what about the player’s statistics, for example his ammo count, or experience, or inventory?

How do we preserve the data, when we can only write code in „Scripts“ – components of Game Objects – which just get destroyed during scene transitions?

Here’s where we get a bit crafty. There IS a way to preserve game objects through scene transitions, effectively building not a scene-wide object, but a game-wide object, which will keep our data.

Here’s the basic workflow of the game using such an object:

2

We need to save the data, transition the scene, and then load the data back.

Here’s the basic breakdown of the logic we will be using:

  • Regardless of the scene we are in (even if it’s scene 1), FIRST initialize the player with starting data.
  • Then, copy over the data from the Global Object.

Global Object initialization logic:

  • Initialize with starting data, and preserve our instance through scene transitions.

What this flow does is ensure that scene 1 will always initialize the player with the starting data. Then, if you save data into the Global Object before any scene transition, you have ensured that this data will always be loaded into the next level’s Player object.

This assumes you have placed the same Player object (preferably a Prefabbed object) into every scene. Note that the „Player object’s initialization logic“ is applicable to any object which needs the illusion of being „preserved through scenes“; we’re only using the Player as the most obvious example.

The code

OK, enough of the graphs and abstract thinking for now. Let’s get to coding.

I am assuming that by this point, you do have two scenes, and an implementation for transitioning between them – refer to the starting project at the top of the article. You’re playing the same player avatar in both, and you just need to save the player’s data between them to give the illusion of the same „player object“.

Let’s first create the game-wide Global Object. It’s important we get this one down right, so let’s figure out what we need it to do:

  • We need to be able to access it from any other script, from any part of the game. A logical choice for it would be the Singleton design concept. If you don’t know what that is, cool, you’re about to learn something new.
  • We need it to only be initialized once, and carry over through the scene transitions.
  • We need it to hold any data we may need to carry over. We know variables we need to save, so we’ll just type those in.

First, go to your first scene, and create a new empty Game Object. Rename it to something fitting, like „GameMaster“ or „GlobalObject“.

Next, create a new C# script (preferably within a new folder – remember to keep things organized). Give it a fitting name. My script’s name is „GlobalControl“.

Attach the new empty C# script to the new Game Object, and open the Script in your editor of choice. MonoDevelop, which comes with Unity, is good, but you can also use Visual Studio.

Put this code into the GlobalControl Script:

public class GlobalControl : MonoBehaviour 
{
    public static GlobalControl Instance;

    void Awake ()   
       {
        if (Instance == null)
        {
            DontDestroyOnLoad(gameObject);
            Instance = this;
        }
        else if (Instance != this)
        {
            Destroy (gameObject);
        }
      }
}

The basic premise of the singleton design pattern is that there is one single public static instance of one class. Within the awake method (one to be called when the object is supposed to be loaded), we are making sure of that by saying „If there is another instance, destroy that one and make sure that the instance is this one“.

Notice a very peculiar function call within the Awake method, “Don’t Destroy On Load”. This is a part of our solution to the problem of cross-scene persistence. This is what will keep the gameObject this script is attached to alive and carry it over to the other scene. The rest of the Singleton concept ensures that if there is another copy of the object with this same script attached (and there will be, you need to put this object into every scene), then the other object will be destroyed and this one (original) will be saved.

If you wish, you can test this now. Put the GameMaster or GlobalObject (or whatever is carrying this script) into every scene you have, and try to transition the scenes at run-time. You will notice there is only one such global object in the scene at any given time.

If we now write data into it, it will be saved!

Now, onto the other part of the problem:
What do we need to save?

For this tutorial’s purpose, suppose your Player has three statistics:

  • HP, with starting value of 100,
  • Ammo, with starting value of 0,
  • XP, with starting value of 0.

These are saved somewhere within your Player object. Which script exactly, doesn’t really matter. We need to have the same variables within our GlobalObject as well, so add them to your code:

public class GlobalControl : MonoBehaviour 
{
//[...]
    public float HP;
    public float Ammo;
    public float XP;
//[...]

Now we are ready to save the data. We only need to save the data when we are transitioning the scene and load it when we are starting a scene.

Here’s how to save the data from the script where you keep your player’s variables:

public class PlayerState : MonoBehaviour 
{
//[...]

    public float HP;
    public float Ammo;
    public float XP;

//[...]

//Save data to global control   
    public void SavePlayer()
    {
        GlobalControl.Instance.HP = HP;
        GlobalControl.Instance.Ammo = Ammo;
        GlobalControl.Instance.XP = XP;
        }
//[...]

It’s wise to build a dedicated function for saving the player data into the instance.
There is now just one more step missing: loading from the GlobalControl. You can easily call that in the Start function of your Player’s State script:

public class PlayerState : MonoBehaviour 
{
//[...]

    public float HP;
    public float Ammo;
    public float XP;

//[...]


    //At start, load data from GlobalControl.
    void Start () 
    {   
        HP = GlobalControl.Instance.HP;
        Ammo = GlobalControl.Instance.Ammo;
        XP = GlobalControl.Instance.XP;
    }
//[...]

With this code, our data flow would look something like this:

3

This goes for every scene transition, universally. Even transitioning back from Scene 2 to Scene 1 will now keep your player’s statistics!

There are, of course, a few kinks to work out. For example, if you quit to main menu and start a new game (without quitting the game altogether), you need to reset the saved data, otherwise you’ll be starting a new game with player stats from the previous session!

Why not public static class?

At this point, if you’re familiar with C# and .NET programming, you might be wondering why we aren’t simply using something like this:

public static class GlobalObject

Contrary to what you might intuitively think, public static classes do not actually persist game-wide. Since any class (that is, any script) is attached to a game object, it will get destroyed when you load a new scene. Even if another scene has a new public static class in it, the data inside will be reset – that is, the static class will be initialized anew at scene load.

This is why we must use the DontDestroyOnLoad method, and a bit more code to ensure we have only one instance of the class we intend to carry across levels.

Polishing and preparing for the next tutorial

You have probably noticed that in this example it’s not hard to manually type the three needed values to GlobalData and back. But what if we have a larger, more complex game with dozens, if not hundreds of player variables to keep track of?

Below, we’ll polish our code to be not just prettier, but to offer additional functionality we’ll explain in the followup tutorial, along with dealing with Save and Load mechanics as well.

First, let’s create a new script within our project. This will be a bit different type of script, because it will not extend the MonoBehavior class nor will it be attached to any object.

We’ll call it “Serializables” and it will look like this:

using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

public class PlayerStatistics
{
    public float HP;
    public float Ammo;
    public float XP;
}

As you can see, there are no functions, no namespaces, only a single class with no constructor, holding our three known variables. Why are we doing this?

So that, in our Global Object, we can turn individual statistics into a single class to contain them:

public class GlobalControl : MonoBehaviour 
{
//[...]
    public float HP;
    public float Ammo;
    public float XP;
//[...]

…like this:

public class GlobalControl : MonoBehaviour 
{
//[...]
    public PlayerStatistics savedPlayerData = new PlayerStatistics();
//[...]

And then the same in the player’s variables:

public class PlayerState : MonoBehaviour 
{
//[...]

    public float HP;
    public float Ammo;
    public float XP;

//[...]

This gives us an additional layer of security. We can’t accidentally write wrong the player variables into saved variables (eg. XP = HP):

public class PlayerState : MonoBehaviour 
{
//[...]
    public PlayerStatistics localPlayerData = new PlayerStatistics();
//[...]

Now, when we want to save the data, we simply remove this:

//Save data to global control   
    public void SavePlayer()
    {
        GlobalControl.Instance.HP = HP;
        GlobalControl.Instance.Ammo = Ammo;
        GlobalControl.Instance.XP = XP;
    }

…and instead, copy the reference to the class that contains our data. All values inside will stay where they are supposed to:

//Save data to global control   
    public void SavePlayer()
    {
                GlobalControl.Instance.savedPlayerData = localPlayerData;        
    }

Same for loading the player data in the player’s start function!

Now we have all our player’s statistics in a class that represents only the player’s data, with nothing extra. During your game’s development, when you need more player variables saved and loaded, simply add them to the class – the saving and retrieving of player data to/from Global Object stays the same.

Conclusion

In the next article, we will go through saving and loading the entire class to the hard drive (not just a Global Object) by using serialization.

If you got stuck at any point or just want to see what the finished project looks like, you can download it here:

[GitHub Repository]
[ZIP Download]

Questions? Comments? Let us know in the area below!


  • http://careersreport.com lisa.bumgarn

    I want to share amazing ^online freelancing opportunity… 3-5 hrs of work /day… Paycheck every week…Extra bonus for job well done…Payment of $6k to$9k a month… Merely several hrs of free time, a computer, basic knowing of# internet and! trusted internet-connection is what is required…Get more info on my page

  • http://careersreport.com Pauline Gary

    Allow me to show you a suberb! way to earn a lot of extra money by finishing basic tasks from your house for few short hours a day — See more info by visiting >MY____$DISQUS$____ID;}

  • Rémi Scovolo

    Cool tutorial dude !
    but having all the variables “hidden” under PlayerStatistics makes testing and game design really hard : you cant change the values runtime in the editor… :/

    Or do I miss something ?

    • Eudaimonium

      They’re not hidden, an instance of PlayerStatistics class is within PlayerState (player’s primary script), called “localPlayerData” – and it’s viewable in Inspector.

      • Rémi Scovolo

        I’m so stupid -_-
        Nevermind !
        Thx ;)

  • facy ben book

    i download the package but still have missing two prefabs

    • Eudaimonium

      Just a sec, looking into it – I believe I forgot to set text-only serialization for saving scenes in Unity, resulting in placement of objects not being saved. I will update the links soon.

      • facy ben book

        even the second tutorial some script missing :/ i was so happy that i found this tutorial but when i got the package found it’s missing a lot of things

        • Adriaan Rijkens

          Same here? Any update on the project? Would be great because I really like the article but the project is not working.

      • facy ben book

        in the player i found two missing script i didn t know which one should i put

  • facy ben book

    any updates ? fixes ?

  • facy ben book

    i m using now unity 5.2.2 i got this message now : Assets/Scripts/GlobalControl.cs(7,19): error CS0234: The type or namespace name `SceneManagement’ does not exist in the namespace `UnityEngine’. Are you missing an assembly reference?

    • Eudaimonium

      Ahh yes, SceneManagement did not exist before Unity 5.3, it was Application.LoadLevel before that.

      Just remove the “using UnityEngine.SceneManagement” line at the start of GlobalControl.

      I’ll upload a revised version to Github in a moment.

    • Eudaimonium

      Done, just re-visit the github link again to download new ZIP file if you want to double-check.

      • facy ben book

        thank you i will tell you if encouter something new

  • Efraim Martinez

    Thanks for the ideas. There is something I don’t understand in the workflow.
    When the PlayerState.Start() is executed on the first scene, it loads the data from the GlobalControl, is this right? If so, how can be guaranteed that the GlobalControl data is already initialized?

    • Eudaimonium

      Two ways:

      One – All Awake functions are always executed before Start functions.

      Two – Script ordering. In Edit, Project Settings -> Script Execution order you can change the order of priority of the scripts. Make sure GlobalControl is at the top of the list before “default time” and it’s Awake will be before all other Awakes, and it’s Start will be before all other Starts (and Update and all other functions)

      • Efraim Martinez

        Wow, thanks for so fastest response! That was what I was looking for.

  • Brogan

    Hey this article is great! Got me thinking outside the box. I am wondering though, is it possible to have three scenes running concurrently? The user (in my game) will be controlling 6 characters, I want to know if by using this method will the stats of the characters in the old scene still continue to calculate… eg. fatigue diminishing. So the user can switch between all 3 levels and the units stats will be updated in real time. – Though on second read, the Serializables may be best option for this… Am I on the right track?

  • Kevin Tan

    Hey this article is superb, but the github file that you gave is missing script in every component, can you fix that? It’ll be awesome if you fix this soon, thanks anyway for the great article

  • Sergey P.A.

    Thank you, very useful info!
    One moment. As I know (and checked) we can use public static classes. Because they aren’t attached to GameObjects, they don’t reset their values after loading other scene.

    • Eudaimonium

      Yes, but you also cannot see them in the inspector and debug your stuff real time.

      Besides, choosing exactly what you want in a save structure (and later, serialized to disk) can be immensely useful. I just found this as a better, “more controllable” approach.

      Of course, nothing preventing you from mixing up and experimenting with these ideas and approaches. That’s why they’re there :D

      • Sergey P.A.

        Thank you for the ideas, I understand and I’m agree with you!

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Front-end, once a week, for free.