Saving/loading a game?

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!

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.

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:

// interface your application will always call to save/load a game
public interface ISaveLoadGame
{
   bool SaveGame(string gamename, Game gameobject);  // load the game object
   Game LoadGame(string gamename); /  save the game object
   list<string> SavedGames();  / get a list of saved games
}

//base class to handle all common functionality for saving/loading game

public abstract class SaveLoadBase : ISaveLoadGame
{
  public string SerializeGameObject(Game gameobject)
      {
      string serialized = "";
      //  serialize code
      return serialized;
      }
  public Game DeSerializeGameObject(string serialized)
      {
      Game gameobject;
      //  deserialize code
      return gameobject;
      }
   public abstract bool SaveGame(string gamename, Game gameobject);
   public abstract Game LoadGame(string gamename);
   public abstract   list<string> SavedGames();
}

// then you individual classes to save the various ways

public class SaveLoadPlayerPrefs
{
   public bool SaveGame(string gamename, Game gameobject)
      {
      PlayerPref[gamename] = SerializeGameObject(gameobject);
     return true;
      }
   public Game LoadGame(string gamename)
      {
      Game gameobject =  DeSerializeGameObject(PlayerPref[name].tostring());
       return gameobject;
      }
     public list<string> SavedGames()
         {
         return list of saved games;
         }
}

public class SaveLoadToDisk
{
   public bool SaveGame(string gamename, Game gameobject)
      {
      FileStream file = open(gamename + ".saved", "w");
      file->write(SerializeGameObject(gameobject));
     return true;
      }
   public Game LoadGame(string gamename)
      {
      FileStream file = open(gamename + ".saved", "r");
      Game gameobject =  DeSerializeGameObject(file->read());
       return gameobject;
      }
     public list<string> SavedGames()
         {
         return list of saved games;
         }
}

// then in init code somewhere

SaveLoadBase saveload = null;
if (webplayer)
   saveload = new SaveLoadPlayerPref();
else
   saveload = new SaveLoadToDisk();

// elsewhere you don't care what platform you are running on

saveload->SaveGame();
saveload->LoadGame();

What’s messy about PlayerPrefs?

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.

PlayerPrefs.SetSting() can store XML and JSON.

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.

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. :stuck_out_tongue:

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.

using UnityEngine;
using System.Collections;

public class PlayerStateManager : MonoBehaviour
{
    public static int score;
    public static int lives;
    public static string level;

    void Awake()
    {
        // Keep this object for as long as the game is running
        DontDestroyOnLoad(this.gameObject);
    }

    // NewGame method
    public static void NewGame()
    {
        // Set score and lives to default value
        score = 0;
        lives = 3;

        // Load first level
        Application.LoadLevel("level_0001");
    }

    // LoadGame method
    public static void LoadGame()
    {
        // Get level name to load
        level = PlayerPrefs.GetString("ContinueGame");
           
        // Get score and lives
        score = PlayerPrefs.GetInt("Score");
        lives = PlayerPrefs.GetInt("Lives");
           
        // Load the level
        Application.LoadLevel(level);
           
        Debug.Log ("Last save loaded successfully");
        Debug.Log ("Score: " + score + ". Lives: " + lives);
    }

    // SaveGame method
    public static void SaveGame()
    {
        PlayerPrefs.SetString("ContinueGame", Application.loadedLevelName);
        PlayerPrefs.SetInt("Score", PlayerStateManager.score);
        PlayerPrefs.SetInt("Lives", PlayerStateManager.lives);

        Debug.Log("Game Saved!");
    }
}

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!

1 Like