Search code examples
c#unity-game-enginesaving-datascriptable-object

Issues with Save/Load System in a Text Based Adventure game made with ScriptableObjects in Unity


I am almost done with my Text Based Adventure Game and am trying to setup a Save/Load system that works. I can't seem to accomplish this.

I have viewed multiple tutorials and I feel that Json may be what I need. Unfortunately I can't seem to get Json code correct for the 'Load' part. I am also concerned that the amount of data I am trying to save is too much for a simple Serialization process OR I am over-complicating this and there is a simpler solution.

This is the data I am tryin to Save/Load. As you can see I have Dictionaries and Lists, as well as a 'Room' ScriptableObject that I am trying to save, namely, the currentRoom the Player is in when the game is saved. The currentRoom is tracked in the GameController.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class SaveData
{
public Room saveRoomNavigation;
public List<string> saveNounsInInventory = new List<string>();
public List<InteractableObject> saveUseableItemList = new 
List<InteractableObject>();
public Dictionary<string, string> saveExamineDictionary = new 
Dictionary<string, string>();
public Dictionary<string, string> saveTakeDictionary = new 
Dictionary<string, string>();
public List<string> saveNounsInRoom = new List<string>();
public Dictionary<string, ActionResponse> saveUseDictionary = new 
Dictionary<string, ActionResponse>();


public SaveData (GameController controller, InteractableItems 
interactableItems)
{
    saveRoomNavigation = controller.roomNavigation.currentRoom;
    saveNounsInInventory = interactableItems.nounsInInventory;
    saveUseableItemList = interactableItems.useableItemList;
    saveExamineDictionary = interactableItems.examineDictionary;
    saveTakeDictionary = interactableItems.takeDictionary;
    saveNounsInRoom = interactableItems.nounsInRoom;
    saveUseDictionary = interactableItems.useDictionary;

}


}

Then this is my Save System code where I am trying to implement the Serialization with Json and binary.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public static class SaveSystem
{
public static void SaveGame (GameController controller, 
InteractableItems interactableItems)
{
    BinaryFormatter formatter = new BinaryFormatter();

    string path = Application.persistentDataPath + "/savegame.rta";
    FileStream stream = new FileStream(path, FileMode.Create);

    SaveData data = new SaveData(controller, interactableItems);

    var json = JsonUtility.ToJson(data);
    formatter.Serialize(stream, json);
    stream.Close();
}

public static SaveData LoadGame()
{
    string path = Application.persistentDataPath + "/savegame.rta";
    if (File.Exists(path))
    {
        BinaryFormatter formatter = new BinaryFormatter();
        FileStream stream = new FileStream(path, FileMode.Open);

        SaveData data = 
JsonUtility.FromJsonOverwrite((string)formatter.Deserialize(stream, 
????));
        //SaveData data = formatter.Deserialize(stream) as 
SaveData;
        stream.Close();

        return data;

    }
    else
    {
        Debug.LogError("Save file not found in " + path);
        return null;
    }
}
}

Where I put the '????' above is where I can't seem to get it to accept the Object I am trying to overwrite, which I thought was 'SaveData' since that is where I pull the information to put into the game when the Player clicks the 'Load' button. It tell me "'SaveData' is a type, which is not valid in the given context"

Here is the code I call when the 'Save' and 'Load' buttons are used, it is within my GameController Class where most everything is tracked during Game Play.

public void SaveGame()
{
    SaveSystem.SaveGame(this, interactableItems);
}

public void LoadGame()
{
    SaveData data = SaveSystem.LoadGame();

    roomNavigation.currentRoom = data.saveRoomNavigation;

    interactableItems.nounsInInventory = data.saveNounsInInventory;
    interactableItems.useDictionary = data.saveUseDictionary;
    interactableItems.takeDictionary = data.saveTakeDictionary;
    interactableItems.examineDictionary = 
    data.saveExamineDictionary;
    interactableItems.useableItemList = data.saveUseableItemList;
    interactableItems.nounsInRoom = data.saveNounsInRoom;


    DisplayRoomText();
    DisplayLoggedText();

}

When used in game, I get "SerializationException" errors, so that is when I attempted to try Json. I read where it can serialize a lot more than the basic Unity Serializer. The errors went away and it seems to save OK with Json, but now I am unable to get the Load syntax correct.

I am a novice so maybe I am making this harder then it has to be... I also looked into Userprefs, but I don't think I can convert the Scriptable Object, Room, into a type String.

Thanks in advance!

[UPDATE] Here is what I am able to have it save in .txt format. This proves that it is saving, just not sure "instanceIDs" will give me the Load that I need.

ˇˇˇˇö{"saveRoomNavigation": {"instanceID":11496},"saveNounsInInventory": ["sword"],"saveUseableItemList":[{"instanceID":11212}, {"instanceID":11588},{"instanceID":10710},{"instanceID":11578}, {"instanceID":10718},{"instanceID":11388}, {"instanceID":11276}],"saveNounsInRoom":["rocks","hole"]}


Solution

  • Take a look at the documentation for Unity's built-in JsonUtility:

    https://docs.unity3d.com/Manual/JSONSerialization.html

    It says types such as "Dictionaries" are not supported. Seeing as though you have several Dictionaries in your SaveData example, might I suggest using a more robust JSON library such as JSON.NET, which is free on the asset store:

    https://assetstore.unity.com/packages/tools/input-management/json-net-for-unity-11347