Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Problem Saving Game State

Discussion in 'Scripting' started by OldManBrian, Sep 27, 2014.

  1. OldManBrian

    OldManBrian

    Joined:
    Sep 25, 2014
    Posts:
    3
    My goal here is to create a framework for holding all of my game's variables that would need to persist between scenes as well as from\to saved games.

    I'm working off of the base that This Unity Training Video explained, however the problem I have with it is that it is relatively simple in its example and forces you to create a serializable copy of every global variable you want to save and then you need to copy the data from every variable over/back in order to properly save/load.

    I'm wanting to cut around this by just using serializable nested classes that contain all my variables and just save/load directly to/from that main GameState serializable class. This isn't working though, and I'm not sure if this is even the best way to do this since I'm a beginner:


    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4. using System.Runtime.Serialization.Formatters.Binary;
    5. using System.IO;
    6.  
    7. public class GameGlobals : MonoBehaviour {
    8.  
    9.     public static GameGlobals Globals;
    10.  
    11.  
    12.     void Awake () {
    13.  
    14.         if (Globals == null)
    15.         {
    16.             DontDestroyOnLoad(gameObject);
    17.             Globals = this;
    18.         }
    19.         else if (Globals != this)
    20.         {
    21.             Destroy(gameObject);
    22.         }
    23.  
    24.  
    25.     }
    26.      
    27.     public void Save()
    28.     {
    29.         string SaveFile = Application.persistentDataPath + "/Saves/Save" + Globals.GameState.Game.SaveSlot + ".dat";
    30.      
    31.         BinaryFormatter bf = new BinaryFormatter ();
    32.         FileStream file = File.Create(SaveFile);
    33.      
    34.         bf.Serialize (file, Globals.GameState);
    35.         file.Close();
    36.     }
    37.  
    38.     public void Load()
    39.     {
    40.         string SaveFile = Application.persistentDataPath + "/Saves/Save" + Globals.GameState.Game.SaveSlot + ".dat";
    41.      
    42.         if (File.Exists (SaveFile)) {
    43.             BinaryFormatter bf = new BinaryFormatter();
    44.             FileStream file = File.Open(SaveFile,FileMode.Open);
    45.          
    46.             Globals.GameState = (GameState)bf.Deserialize(file);
    47.          
    48.             file.Close ();
    49.         }
    50.      
    51.      
    52.      
    53.     }
    54.  
    55.     [Serializable]
    56.     public class GameState {
    57.      
    58.         public class Game {
    59.             //Game Data
    60.             public int SaveSlot = 0;
    61.          
    62.         }
    63.      
    64.         public class Player {
    65.             //Player Data
    66.             public string Name = string.Empty;
    67.             public string CodeName = string.Empty;
    68.             public int Rank = 99;
    69.             public int Money = 0;
    70.             public int Standing = 0;
    71.             public int RepKill = 0;
    72.         }
    73.      
    74.      
    75.      
    76.      
    77.     }
    78.  
    79. }
    80.  
    81.  
     
  2. OldManBrian

    OldManBrian

    Joined:
    Sep 25, 2014
    Posts:
    3
    Been at this for hours, no matter what I do I can't set or retrieve values from the GameState class.

    I try getting GameGlobals.Globals.GameState.Game.SaveSlot for example, and I get an error: "'Game': cannot reference a type through and expression".
     
  3. OldManBrian

    OldManBrian

    Joined:
    Sep 25, 2014
    Posts:
    3
    Also intellisense doesnt seem to work with this custom class for some reason.
     
  4. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    Perhaps you need an instance of GameState to save/load data to/from...
     
  5. SquigglyFrog

    SquigglyFrog

    Joined:
    Jan 9, 2014
    Posts:
    25
    The way I do that when I want a global class is like this.. It may not be right, but I get access to my globals wherever I need them..

    GameGlobals globals;

    up in your declarations, then in your OnStart do this:

    globals = GameObject.FindObjectOfType(typeof(GameGlobals)) as GameGlobals;

    Then anywhere you need to access it, just use your globals.function .. Hope this helps some!! May not, cuz I may have read the original problem wrong, but hey.. =D
     
  6. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Hm, is it your intention to use nested classes?
    Also, you may want to make sure the directory exists, it has thrown an error to me because i didn't have a folder called Saves.

    Anyway, I adjusted the code a bit, you can take out the GUI methods, they were only for testing purposes.
    Before you do that, start the scene and save the data once. Afterwards quit playing, change some attributes such as the rank of the player and start again.
    The new value should be shown in the console as long as you don't click the load button. It should change to the saved one afterwards.

    I was too lazy to try if every attribute gets serialized correctly, but i think it works quite as you want.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4. using System.Runtime.Serialization.Formatters.Binary;
    5. using System.IO;
    6.  
    7. public class GameGlobals : MonoBehaviour
    8. {
    9.     public static GameGlobals Globals;
    10.     public GameState gameState;
    11.  
    12.     void Awake()
    13.     {
    14.         if (Globals == null)
    15.         {
    16.             DontDestroyOnLoad(gameObject);
    17.             gameState = new GameState();
    18.             Globals = this;
    19.         }
    20.         else if (Globals != this)
    21.         {
    22.             Destroy(gameObject);
    23.         }
    24.     }
    25.  
    26.     public void Save()
    27.     {
    28.         string SaveFile = Application.persistentDataPath + "/Saves/Save" + Globals.gameState.game.SaveSlot.ToString() + ".dat";
    29.  
    30.         BinaryFormatter bf = new BinaryFormatter();
    31.         FileStream file = File.Create(SaveFile);
    32.  
    33.         bf.Serialize(file, Globals.gameState);
    34.         file.Close();
    35.     }
    36.  
    37.     public void Load()
    38.     {
    39.         string SaveFile = Application.persistentDataPath + "/Saves/Save" + Globals.gameState.game.SaveSlot.ToString() + ".dat";
    40.  
    41.         if (File.Exists(SaveFile))
    42.         {
    43.             BinaryFormatter bf = new BinaryFormatter();
    44.             FileStream file = File.Open(SaveFile, FileMode.Open);
    45.  
    46.             Globals.gameState = (GameState)bf.Deserialize(file);
    47.             file.Close();
    48.         }
    49.     }
    50.  
    51.     void OnGUI()
    52.     {
    53.         Debug.Log(Globals.gameState.player.Rank);
    54.         if(GUILayout.Button("Save"))
    55.             Save();
    56.         if(GUILayout.Button("Load"))
    57.             Load();
    58.     }
    59.  
    60.     [Serializable]
    61.     public class GameState
    62.     {
    63.         public Game game;
    64.         public Player player;
    65.  
    66.         public GameState()
    67.         {
    68.             game = new Game();
    69.             player = new Player();
    70.         }
    71.  
    72.         [Serializable]
    73.         public class Game
    74.         {
    75.             //Game Data
    76.             public int SaveSlot = 0;
    77.         }
    78.  
    79.         [Serializable]
    80.         public class Player
    81.         {
    82.             //Player Data
    83.             public string Name = string.Empty;
    84.             public string CodeName = string.Empty;
    85.             public int Rank = 99;
    86.             public int Money = 0;
    87.             public int Standing = 0;
    88.             public int RepKill = 0;
    89.         }
    90.     }
    91. }
     
    Last edited: Sep 27, 2014
    Fraconte likes this.
  7. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    With his system he just need to type GameGlobals.Globals to access all the globals. No need to Find anything because of the static Globals.
     
  8. SquigglyFrog

    SquigglyFrog

    Joined:
    Jan 9, 2014
    Posts:
    25
    Yeah, im at work, think i misread the code and post while flipping between work and here.. no harm done
     
  9. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    For quickly testing the code you can also use this OnGUI code, it may provide a better overview than the console even though it looks crappy xD

    Code (CSharp):
    1. void OnGUI()
    2.     {
    3.         if(GUILayout.Button("Save"))
    4.             Save();
    5.         if(GUILayout.Button("Load"))
    6.             Load();
    7.         GUILayout.Label("Game.SaveSlot: " + Globals.gameState.game.SaveSlot.ToString());
    8.         GUILayout.Label("Name: " + Globals.gameState.player.Name);
    9.         GUILayout.Label("CodeName: " + Globals.gameState.player.CodeName);
    10.         GUILayout.Label("Rank: " + Globals.gameState.player.Rank);
    11.         GUILayout.Label("Money: " + Globals.gameState.player.Money);
    12.         GUILayout.Label("RepKill: " + Globals.gameState.player.RepKill);
    13.         GUILayout.Label("Standing: " + Globals.gameState.player.Standing);
    14.     }
     
  10. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    It seems you don't need the new GameState() and the public GameState(). The first also blanks the values put in the editor.
    I would be glad to know if there are cases where it's needed...
     
  11. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    I used the constructor in order to prevent nullreference exceptions on gamestate when trying to save it ... of course you may want to use the load method autmatically before you can even save the default stats and lose the progress. :p
     
  12. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    Without the constructor the first time you click save it saves the values you have put in the editor. I think the [Serializable] cause the automatic instantiation of GameState.
     
    Suddoha likes this.
  13. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Gonna try that later. Thanks for the hint.

    *edit Fraconte is right, no constructor needed.
     
    Last edited: Sep 28, 2014