Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Is there a more convenient way to manage player data across multiple scenes?

Discussion in 'Scripting' started by DroidifyDevs, Jul 6, 2020.

  1. DroidifyDevs

    DroidifyDevs

    Joined:
    Jun 24, 2015
    Posts:
    1,724
    I need to keep track of things such as player coins, challenge progress, car ownership etc... across the main menu scene, store scene, game scene and so on (I'm also saving this data with JSON). Therefore, I came up with a simple solution: Make a gameobject with all those necessary scripts in the main menu and set it up with DontDestroyOnLoad. Any script that needs to use it can simply call GameObject.FindWithTag("FinancialManager").GetComponent<TheNeededScript>, which is super easy and not the worst performant approach. Overall, in terms of ease of use and simplicity, this has been an ideal solution to my problem.

    However, it is very annoying during development because any time I'm in a scene that's not the main menu, pretty much all the scripts that need the FinancialManager are broken because that object hasn't been loaded (because as a developer I can load each scene without needing to load the main menu).

    Is there a better way to do this? It is not a huge deal, but it does mean that every time I press play I need to enter the main menu first. Thank you for any ideas.
     
  2. WarmedxMints

    WarmedxMints

    Joined:
    Feb 6, 2017
    Posts:
    1,035
    I use scriptable objects for things like that myself. The only thing you need to consider is that data modified within them when in the editor, be it during play mode or not is saved while it isn't in a build.

    If that is going to be an issue, you can code it to create an instance when working in the editor.

    Here's a good talk on using them to talk between classes -
     
    DroidifyDevs likes this.
  3. DroidifyDevs

    DroidifyDevs

    Joined:
    Jun 24, 2015
    Posts:
    1,724
    I've heard about them before but never looked into what it was. Thanks!

    I was thinking a quick hack for my method would be to spawn the FinancialManager in whatever scene I load and then prevent it from spawning again in another scene.
     
  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,932
    DontDestroyOnLoad is such a janky feature. It should be a permanent, special area of the hierarchy to place objects that is independent of scenes. Instead, Unity makes us put the object in a scene and call the method.

    Anyway I get past it by making my GameManager object a prefab and placing it into every scene. The Singleton pattern I use will destroy the duplicates.
     
  5. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,569
    You can just use DontDestroyOnLoad once for a 'folder' gameobject and all of the children will never die. You can also just create a 'base' scene that starts up first and is responsible for loading/unloading all of the other scenes additively which is the proper way to manage them - because it's actual management of scenes and not just hope.
     
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,342
    That only works if TheNeededScript is a singleton - you want it to always be there, and you want there to only be one, which is statically accessible. If it is, turn it into an actual singleton, which you access through TheNeededScript.instance.

    The easiest way to make that work across scenes is to add it to a prefab, and stuff that prefab into resources. Then you turn the singleton into a lazily loaded thing;

    Code (csharp):
    1. public class TheNeededScript : MonoBehaviour {
    2.     private static TheNeededScript _instance;
    3.     public static TheNeededScript instance {
    4.         get {
    5.             if (_instance == null) {
    6.                 _instance = Instantiate(Resources.Load<TheNeededScript>("path_to_prefab"));
    7.                 DontDestroyOnLoad(_instance.gameObject);
    8.             }
    9.             return _instance;        
    10.     }
    11.  
    12.     public int neededValue;
    13. }
    14.  
    15. // old version that breaks if the correct scene wasn't loaded at first:
    16. var value = GameObject.FindWithTag("FinancialManager").GetComponent<TheNeededScript>().neededValue;
    17.  
    18. // new version, works always:
    19. var value = TheNeededScript.instance.neededValue;
     
  7. DroidifyDevs

    DroidifyDevs

    Joined:
    Jun 24, 2015
    Posts:
    1,724
    So I seem to be having success with a method I was using initially to prevent the DontDestroyOnLoad object from being duplicated if you loaded another scene and then came back to the main menu.

    I simply put the exact same prefab in every scene. Then in one of the scripts, I do this:

    Code (CSharp):
    1. public static bool created;
    2.  
    3. void Awake()
    4. {
    5.     if (!created)
    6.     {
    7.         DontDestroyOnLoad(gameObject);
    8.         created = true;
    9.     }
    10.     else
    11.         Destroy(gameObject);
    12. }
    I am not sure what the potential downsides of this method are or what's the technical name for it, but initially I have noticed no issues.
     
  8. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    You can have all your scenes check if the FinancialManager object exists, and if it doesn't then instantiate one with default settings. That way you can keep using the pattern you are already using, and all your scenes will just work.