[SOLVED] Need a Save Game Example

Google is your friend, just substitute xna for monogame.

Official Microsoft source: https://msdn.microsoft.com/en-us/library/bb203924.aspx


https://gavindraper.com/2010/11/25/how-to-loadsave-game-state-in-xna/
http://xnafan.net/2012/07/saving-and-loading-data-in-a-xna-windows-game-xmlserialization/

Thank you for the quick response @kosmonautgames I have spent quite a few hours googling and even found some of the tutorials you mentioned but most seemed to use assemblies that no longer works with Visual Studio 2017 & Monogame 3.6 (or at least not for me) such as Microsoft.Xna.Framework.Storage Or Microsoft.Xna.Framework.GamerServices … perhaps I am missing something.

The final link looks more promising so will watch/read the tutorial and give it a go.

Thanks once again

sorry then, i wasnt aware.

You can also encode your own save data by using the BinaryWriter class for example

I should really write a bare bones version of what I did so I can share it easily, but here’s the concept:

I have two things going on here. The first is a Struct of serializable objects that I turn into XML and save using IsolatedStorage. The second is a class that I use to hold “SaveFile” data, like knowing if a slot is empty or not.

Here’s the first:

using System.IO.IsolatedStorage;
using System.Xml.Serialization;


XmlSerializer serializer;
Player player;
int scoreTemp, experienceTemp, clownsFreedTemp;
Boolean playerHasShoesOnTemp;
List<SoccerBallWrapper> ownedSoccerBallsTemp; 
//SoccerBallWrapper would be a custom wrapper class that I wrote to store data. 
//Basically just a stripped down class that holds information about a GameObject that isn't serializable.
//For example, if you wanted to save the player's inventory of soccer balls, but the SoccerBall class
//has a bunch of methods and stuff. The wrapper would hold only data that needs to be saved

string containerName = "GameSaves";
SaveFile selectedSaveFile;

[Serializable]
public struct SaveGame
{
    int score, experience, clownsFreed;
    Boolean playerHasShoesOn;
    List<SoccerBallWrapper> ownedSoccerBalls; 
}

public SaveLoadManager(Player p)
{
player = p;
serializer = new XmlSerializer(typeof(SaveGame));
ownedSoccerBallsTemp = new List<SoccerBallWrapper>();
}

void SaveToDevice()
{
    var dataFile = Global.FileManager.GetDefaultIsolatedStorageFileStore();
    IsolatedStorageFileStream isolatedFileStream = null;

    //Copy all game data that you want to save over to the Temp variables you created above.

    this.scoreTemp = player.score;
    this.experienceTemp = player.experience;

    //Loop through player.ownedSoccerBalls and create a wrapper for it, then add it to ownedSoccerBallsTemp
    //...
    //...

    //Add it to a new SaveGame object
    SaveGame SaveData = new SaveGame()
    {
        experience = experienceTemp,
        score = scoreTemp,
        ownedSoccerBalls = ownedSoccerBallsTemp,
        ...
    }

    //Save it
    if (dataFile.FileExists(selectedSaveFile.fileName))
    {
        dataFile.DeleteFile(selectedSaveFile.fileName);
    }
    using (isolatedFileStream = dataFile.CreateFile(selectedSaveFile.fileName))
    {
        // Set the position to the begining of the file.
        isolatedFileStream.Seek(0, SeekOrigin.Begin);
        // Serialize the new data object.
        serializer.Serialize(isolatedFileStream, SaveData);
        // Set the length of the file.
        isolatedFileStream.SetLength(isolatedFileStream.Position);

    }

    dataFile.Close();
    isolatedFileStream.Dispose();
}

void LoadFromDevice()
{
     var dataFile = Global.FileManager.GetDefaultIsolatedStorageFileStore();
     IsolatedStorageFileStream isolatedFileStream = null;

     if (dataFile.FileExists(selectedSaveFile.fileName))
     {

        // Open the file using the established file stream.
        using (isolatedFileStream = dataFile.OpenFile(selectedSaveFile.fileName, FileMode.Open, FileAccess.ReadWrite))
        {
            // Store the deserialized data object.
            SaveGame SaveData = (SaveGame)serializer.Deserialize(isolatedFileStream);
            
            //Extract the save data
            player.score = SaveData.score;
            player.experience = SaveData.experience;

            //Loop through SaveData.ownedSoccerBalls and use the wrapper data to recreate the player's ownedSoccerBalls
        }
        dataFile.Close();
        isolatedFileStream.Close();
    }
}

That should take care of saving and loading. I did a lot of redundant things there to make the concept more clear. There’s no reason to create temporary variables for basic types like integers or booleans, or anything you can save directly, really. You can just copy them directly to the SaveGame struct.

As for the second part, the SaveFile class:

    public class SaveFile
    {
        public Boolean empty = true;
        public String fileName;
        public int score, experience, whatever;
        public DateTime rawDateTime;
    

       public void PrepareSavePreview(SaveGame SaveData)
       {
       // Use this to set your stats (score, experience, whatever). 
       // this.score = SaveData.score
       }
  }

I use this class to tell if a save slot is empty and to display some basic stats on the Save/Load game screen. Then I create a List in the first section I showed above.

If you want 3 Save Slots in your game, you would create 3 SaveFiles to add to that list and give them fileNames like “1.sav”, “2.sav”, “3.sav”. When a player starts a new game you set the “selectedSaveFile” attribute from that first section to whatever slot they chose. Then you can save to the “selectedSaveFile.fileName”. This way, when they play the game later, you can loop through however many save slots your game has…

foreach(SaveFile file in SaveFiles)
{
 if(file.empty == false)
    //Show some save data on screen and let them load it!
 else
    //Empty slot!
}

Welp, that was a lot. Hope it helps. I’ll clarify anything if you need it. By the way, I based this originally off of https://gavindraper.com/2010/11/25/how-to-loadsave-game-state-in-xna/ which I read years ago. Then I modified it to work better with Monogame and my own purposes.

Edit: I should also add I have no idea how well this would work with mobile ports. We had to change some stuff for UWP and I’ll most likely have to change things for other consoles. It’s a start, anyway.

2 Likes

Thanks @gap9269. This looks much more elegant than some of the other solutions I have looked at. I’ll have a go at implementing something very similar to this. Thanks once again

Hey,
I have to do similar work. Do you still have your code somewhere?
Would be really nice, cause the documentation of monogame is not so big :wink:

There are several ways you can approach your requirement for a save of your game’s user data.

If you want (or require) that you save an object as serialized data than the examples that people are positing should be more than fill your requirements. However, the question is, do you want to save object data? If so, than these suggestions are on par with doing so.

If however, you want to save the data as key-valued pairs than using a simple text file formatted similar to the following would be a less complex was of doing this…

KEY-ID=VALUE

If you believe you are going to be saving more than just user data for your game, you may want to consider an embedded database such as SQLIte or the Firebird Embedded Database. In this case your user data would simply be saved into a table reflecting this information.

If you are interested in items #2 or #3, I could provide some sample code for you… :relaxed: