What is the proper way to handle data between scenes?

  • I am developing my first 2D game in Unity and I have come across what seems an important question.

    How do I handle data between scenes?

    There seems to be different answers to this:

    • Someone mention using PlayerPrefs, while other people told me this should be used to store other things like screen brightness and so on.

    • Someone told me that the best way was to make sure to write everything into a savegame everytime that I changed scenes, and to make sure that when the new scene loads, get the info from the savegame again. This seemed to me wasteful in performance. Was I wrong?

    • The other solution, which is the one I have implemented so far is to have a global game object that isn't destroyed between scenes, handling all the data between scenes. So when the game starts, I load a Start Scene where this object is loaded. After this ends, it loads the first real game scene, usually a main menu.

    This is my implementation:

    using UnityEngine;
    using UnityEngine.UI;
    using System.Collections;
    
    public class GameController : MonoBehaviour {
    
        // Make global
        public static GameController Instance {
            get;
            set;
        }
    
        void Awake () {
            DontDestroyOnLoad (transform.gameObject);
            Instance = this;
        }
    
        void Start() {
            //Load first game scene (probably main menu)
            Application.LoadLevel(2);
        }
    
        // Data persisted between scenes
        public int exp = 0;
        public int armor = 0;
        public int weapon = 0;
        //...
    }
    

    This object can be handled on my other classes like this:

    private GameController gameController = GameController.Instance;
    

    While this has worked so far, it presents me with one big problem: If I want to load directly a scene, let's say for instance the final level of the game, I can't load it directly, since that scene does not contain this global game object.

    Am I handling this problem the wrong way? Are there better practices for this kind of challenge? I would love to hear your opinions, thoughts and suggestions on this issue.

    Thanks

  • Listed in this answer are the fundamental ways of handling this situation. Although, most of these methods do not scale well to large projects. If you want something more scalable and are not afraid of getting your hands dirty, check out the answer by Lea Hayes about Dependency Injection frameworks.


    1. A static script for holding data only

    You can create a static script to hold data only. Since it is static, you don't need to assign it to a GameObject. You can simply access your data like ScriptName.Variable = data; etc.

    Pros:

    • No instance or singleton required.
    • You can access data from everywhere in your project.
    • No extra code to pass values between scenes.
    • All variables and data in a single database-like script make it easy to handle them.

    Cons:

    • You will not be able to use a Coroutine inside the static script.
    • You will probably end up with huge lines of variables in a single class if you don't organize well.
    • You can't assign fields/variables inside the editor.

    An Example:

    public static class PlayerStats
    {
        private static int kills, deaths, assists, points;
    
        public static int Kills 
        {
            get 
            {
                return kills;
            }
            set 
            {
                kills = value;
            }
        }
    
        public static int Deaths 
        {
            get 
            {
                return deaths;
            }
            set 
            {
                deaths = value;
            }
        }
    
        public static int Assists 
        {
            get 
            {
                return assists;
            }
            set 
            {
                assists = value;
            }
        }
    
        public static int Points 
        {
            get 
            {
                return points;
            }
            set 
            {
                points = value;
            }
        }
    }
    

    2. DontDestroyOnLoad

    If you need your script to be assigned to a GameObject or derive from MonoBehavior, then you can add DontDestroyOnLoad(gameObject); line to your class where it can be executed once (Placing it in Awake() is usally the way to go for this).

    Pros:

    • All MonoBehaviour jobs (for example Coroutines) can be done safely.
    • You can assign fields inside the editor.

    Cons:

    • You will probably need to adjust your scene depending on the script.
    • You will probably need to check which secene is loaded to determine what to do in Update or other general functions/methods. For example, if you are doing something with UI in Update(), then you need to check if correct scene is loaded to do the job. This causes loads of if-else or switch-case checks.

    3. PlayerPrefs

    You can implement this if you also want your data to be stored even if the game gets closed.

    Pros:

    • Easy to manage since Unity handles all background process.
    • You can pass data not only between scenes but also between instances (game sessions).

    Cons:

    • Uses file system.
    • Data can easily be changed from prefs file.

    4. Saving to a file

    This is a bit overkill for storing values between scenes. If you don't need encryption, I discourage you from this method.

    Pros:

    • You are in control of data saved as opposed to PlayerPrefs.
    • You can pass data not only between scenes but also between instances (game sessions).
    • You can transfer the file (user-generated content concept relies on this).

    Cons:

    • Slow.
    • Uses file system.
    • Possibility of reading/loading conflicts caused by stream interruption while saving.
    • Data can easily be changed from the file unless you implement an encryption (Which will make the code even slower.)

    5. Singleton pattern

    Singleton pattern is a really hot topic in object-oriented programming. Some suggest it, and some don't. Research it yourself and make the appropriate call depending on your project's conditions.

    Pros:

    • Easy to both set-up and use.
    • You can access data from everywhere in your project.
    • All variables and data in a single database-like script make it easy to handle them.

    Cons:

    • Lots of boilerplate code whose only job is to maintain and secure the singleton instance.
    • There are strong arguments against the use of singleton pattern. Be cautious and make your research beforehand.
    • Possibility of data clash due to poor implementation.
    • Unity may have a difficulty handling singleton patterns1.

    1: In the summary of OnDestroy method of Singleton Script provided in Unify Wiki, you can see the author describing ghost objects which bleed into the editor from runtime:

    When Unity quits, it destroys objects in a random order. In principle, a Singleton is only destroyed when application quits. If any script calls Instance after it have been destroyed, it will create a buggy ghost object that will stay on the Editor scene even after stopping playing the Application. Really bad! So, this was made to be sure we're not creating that buggy ghost object.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM