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

Saving/loading a game?

Discussion in 'Scripting' started by BinaryOrange, Sep 9, 2014.

  1. BinaryOrange

    BinaryOrange

    Joined:
    Jul 27, 2012
    Posts:
    138
    I've been looking into this all morning and I'm getting all kinds of conflicting/non-working tutorials.

    I just want a way to save basic information for my game - such what level the player has saved in, how many points they have, and how many lives they have.
    I thought the best way to do this would be to use the PlayerPrefs class, but that gets kind of messy, and it's not a great way to go about this.

    I want this system to work with a main menu, so if the player clicks on "Continue Game", it will continue from where they last left off.

    If anybody has any advice and can help me, it would be much appreciated!
     
  2. cmcpasserby

    cmcpasserby

    Joined:
    Jul 18, 2014
    Posts:
    315
    you could make your own class or struct to hold all the different bits of data you need, than serialize that to disc using xml or json.
     
  3. AndyLL

    AndyLL

    Joined:
    Aug 25, 2013
    Posts:
    75
    You did not say want platform you are using which changes your options.

    Webplayer - PlayerPrefs or Database
    Desktop - PlayerPrefs, Database, Directly to Disk
    Android - Whatever.

    I would however, use interfaces so you can support multiple methods without having to make major change to your code.

    For a simplified example:

    Code (CSharp):
    1.  
    2. // interface your application will always call to save/load a game
    3. public interface ISaveLoadGame
    4. {
    5.    bool SaveGame(string gamename, Game gameobject);  // load the game object
    6.    Game LoadGame(string gamename); /  save the game object
    7.    list<string> SavedGames();  / get a list of saved games
    8. }
    9.  
    10. //base class to handle all common functionality for saving/loading game
    11.  
    12. public abstract class SaveLoadBase : ISaveLoadGame
    13. {
    14.   public string SerializeGameObject(Game gameobject)
    15.       {
    16.       string serialized = "";
    17.       //  serialize code
    18.       return serialized;
    19.       }
    20.   public Game DeSerializeGameObject(string serialized)
    21.       {
    22.       Game gameobject;
    23.       //  deserialize code
    24.       return gameobject;
    25.       }
    26.    public abstract bool SaveGame(string gamename, Game gameobject);
    27.    public abstract Game LoadGame(string gamename);
    28.    public abstract   list<string> SavedGames();
    29. }
    30.  
    31. // then you individual classes to save the various ways
    32.  
    33. public class SaveLoadPlayerPrefs
    34. {
    35.    public bool SaveGame(string gamename, Game gameobject)
    36.       {
    37.       PlayerPref[gamename] = SerializeGameObject(gameobject);
    38.      return true;
    39.       }
    40.    public Game LoadGame(string gamename)
    41.       {
    42.       Game gameobject =  DeSerializeGameObject(PlayerPref[name].tostring());
    43.        return gameobject;
    44.       }
    45.      public list<string> SavedGames()
    46.          {
    47.          return list of saved games;
    48.          }
    49. }
    50.  
    51. public class SaveLoadToDisk
    52. {
    53.    public bool SaveGame(string gamename, Game gameobject)
    54.       {
    55.       FileStream file = open(gamename + ".saved", "w");
    56.       file->write(SerializeGameObject(gameobject));
    57.      return true;
    58.       }
    59.    public Game LoadGame(string gamename)
    60.       {
    61.       FileStream file = open(gamename + ".saved", "r");
    62.       Game gameobject =  DeSerializeGameObject(file->read());
    63.        return gameobject;
    64.       }
    65.      public list<string> SavedGames()
    66.          {
    67.          return list of saved games;
    68.          }
    69. }
    70.  
    71. // then in init code somewhere
    72.  
    73. SaveLoadBase saveload = null;
    74. if (webplayer)
    75.    saveload = new SaveLoadPlayerPref();
    76. else
    77.    saveload = new SaveLoadToDisk();
    78.  
    79. // elsewhere you don't care what platform you are running on
    80.  
    81. saveload->SaveGame();
    82. saveload->LoadGame();
     
  4. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    What's messy about PlayerPrefs?
     
  5. cmcpasserby

    cmcpasserby

    Joined:
    Jul 18, 2014
    Posts:
    315
    very limited datatypes, no structure and that it just wasn't made for this. PlayerPrefs is for storing prefs not for storing the whole entire save state of a level.
     
  6. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    PlayerPrefs.SetSting() can store XML and JSON.
     
  7. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    The main problem I have with PlayerPrefs for saving data is that it becomes chaos. It makes much more sense to have one class handle saving the game and loading the game. If you use PlayerPrefs for everything, it becomes super easy to spread the responsibility of saving data across many many classes in the game.

    However my SaveService class stores data, I don't really care about. Though I think just converting a data object to JSON makes sense. At that point, using Application.persistentDataPath is just as easy as using PlayerPrefs to store a string, though again at that point I don't really think it matters.

    One thing to keep in mind that for web builds you cannot write to disk and PlayerPrefs is limited to 1 megabyte. Web builds generally have to save to a web-accessible database.
     
  8. BinaryOrange

    BinaryOrange

    Joined:
    Jul 27, 2012
    Posts:
    138
    Thanks for the responses all.

    The reason I was trying to avoid PlayerPrefs was because it's pretty limited, and it's just cumbersome to have to type everything out! However, for the scope of the game I'm making, they may just have to do until I can understand more advanced file I/O. Plus, I'm making this game for the Web Player, so that doesn't help my situation!

    Another reason I was avoiding them was because I was having trouble keeping everything in one place. However, I finally realized I could create a "PlayerStateManager" (like a GameStateManager) and just manage everything from here. Now everything works how I want, sort of. :p

    Here's the script as I have it now, the main problem I'm having is that if the player loses all lives and it goes back to the main menu it's makes ANOTHER PlayerStateManager, this can be solved with a simple check so I'll add that in a bit.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class PlayerStateManager : MonoBehaviour
    5. {
    6.     public static int score;
    7.     public static int lives;
    8.     public static string level;
    9.  
    10.     void Awake()
    11.     {
    12.         // Keep this object for as long as the game is running
    13.         DontDestroyOnLoad(this.gameObject);
    14.     }
    15.  
    16.     // NewGame method
    17.     public static void NewGame()
    18.     {
    19.         // Set score and lives to default value
    20.         score = 0;
    21.         lives = 3;
    22.  
    23.         // Load first level
    24.         Application.LoadLevel("level_0001");
    25.     }
    26.  
    27.     // LoadGame method
    28.     public static void LoadGame()
    29.     {
    30.         // Get level name to load
    31.         level = PlayerPrefs.GetString("ContinueGame");
    32.            
    33.         // Get score and lives
    34.         score = PlayerPrefs.GetInt("Score");
    35.         lives = PlayerPrefs.GetInt("Lives");
    36.            
    37.         // Load the level
    38.         Application.LoadLevel(level);
    39.            
    40.         Debug.Log ("Last save loaded successfully");
    41.         Debug.Log ("Score: " + score + ". Lives: " + lives);
    42.     }
    43.  
    44.     // SaveGame method
    45.     public static void SaveGame()
    46.     {
    47.         PlayerPrefs.SetString("ContinueGame", Application.loadedLevelName);
    48.         PlayerPrefs.SetInt("Score", PlayerStateManager.score);
    49.         PlayerPrefs.SetInt("Lives", PlayerStateManager.lives);
    50.  
    51.         Debug.Log("Game Saved!");
    52.     }
    53. }
    54.  
    This makes everything so much easier, as now I can call "PlayerStateManager.LoadGame();" from the main menu, and it loads perfectly.

    So for this game this method will do, however I will research serialization further for more complex games!
     
    GarthSmith likes this.