Serializing inventory with my custom save system

Moodacow

New member
Hello
I was trying to serialize the inventory items but it doesn't work because item definition can't be serialized.

so I want to know how I should do it or should I make my own Serialization? or can I just pass it to your save system and serialize it from there and pass it to my save system? but I'm not sure how to do it.

My save system is based on this video and it worked well for me.
 
Interesting, that's similar to our save system.

Ah good example is from pixel crushers:
Code:
        public int saveSlot = 0;

        public override string RecordData()
        {
            SaveSystemManager.Save(saveSlot);
            return SaveSystem.Serialize(SaveSystemManager.GetCurrentSaveData());
        }

        public override void ApplyData(string s)
        {
            if (string.IsNullOrEmpty(s)) return;
            var data = SaveSystem.Deserialize<SaveData>(s);
            if (data == null) return;
            var inventoryMonitor = FindObjectOfType<InventoryMonitor>();
            var originalMonitorValue = (inventoryMonitor != null) ? inventoryMonitor.enabled : false;
            if (inventoryMonitor != null) inventoryMonitor.enabled = false;
            SaveSystemManager.SetCurrentSaveData(data);
            SaveSystemManager.Load(saveSlot);
            if (inventoryMonitor != null) inventoryMonitor.enabled = originalMonitorValue;
        }

        public override void OnRestartGame()
        {
            SaveSystemManager.DeleteSave(saveSlot);
        }
This essentially uses our save system inside of yours.

If you do not want to use our save system at all, then another option is having a look at all our scripts inheriting "SaverBase". These are the components we use to save items, currency, etc... You can inspire yourself from those scripts to make your own saver components.

After looking a the code carefully again, I realize that the code above writes the UIS save data to disk. I will add some more functions to allow you to save and get the serialized data without writing to disk, in the next update. That should help nesting our save system in other save systems be more efficient.

EDIT: The next update will be a major update and I will update the documentation intensively. I'll make sure to include some examples for your use case once I do that.
 
The next update is planned for early next month.
Its up to you whether you wish to wait. If I were you I'd use the code above as a starting point to test things out already and once the update becomes live you can update your code to use the new save methods.
 
Interesting, that's similar to our save system.

Ah good example is from pixel crushers:
Code:
        public int saveSlot = 0;

        public override string RecordData()
        {
            SaveSystemManager.Save(saveSlot);
            return SaveSystem.Serialize(SaveSystemManager.GetCurrentSaveData());
        }

        public override void ApplyData(string s)
        {
            if (string.IsNullOrEmpty(s)) return;
            var data = SaveSystem.Deserialize<SaveData>(s);
            if (data == null) return;
            var inventoryMonitor = FindObjectOfType<InventoryMonitor>();
            var originalMonitorValue = (inventoryMonitor != null) ? inventoryMonitor.enabled : false;
            if (inventoryMonitor != null) inventoryMonitor.enabled = false;
            SaveSystemManager.SetCurrentSaveData(data);
            SaveSystemManager.Load(saveSlot);
            if (inventoryMonitor != null) inventoryMonitor.enabled = originalMonitorValue;
        }

        public override void OnRestartGame()
        {
            SaveSystemManager.DeleteSave(saveSlot);
        }
This essentially uses our save system inside of yours.

If you do not want to use our save system at all, then another option is having a look at all our scripts inheriting "SaverBase". These are the components we use to save items, currency, etc... You can inspire yourself from those scripts to make your own saver components.

After looking a the code carefully again, I realize that the code above writes the UIS save data to disk. I will add some more functions to allow you to save and get the serialized data without writing to disk, in the next update. That should help nesting our save system in other save systems be more efficient.

EDIT: The next update will be a major update and I will update the documentation intensively. I'll make sure to include some examples for your use case once I do that.
sorry question how can I use the code above with my own scripts? im a little bit confused..
 
In the video you metionned in your first post they explain how to create saveable scripts.

In those you have two functions "CaptureState" and "RestoreState".
Capture State is used to save the current state of that object. In your case you'll want to save the entire state of the Inventory System. So in the code above "RecordData" is Equivalent to "CaptureState" in our system we call it "SerializeSaveData". All of these names mean the same thing.

In your "CaptureState" function you return an object. that object must be the serialized save data. So similar to the code above you save first and then return the save data
Code:
SaveSystemManager.Save(saveSlot);
return SaveSystemManager.GetCurrentSaveData();

The within your "RestoreState" function you'll want to load the inventory save data. So you'll do:
Code:
SaveSystemManager.SetCurrentSaveData(data);
SaveSystemManager.Load(saveSlot);
where data is the object you saved previously. If course you'll need to cast the object and make sure it isn't null.

I hope that helps
 
I tried working on it and I think I can understand it a little bit but I have a problem with converting to my object form.. I tried a lot of things and I still cant manage to do it

I tried to make a new struct with SaveData in it and it didn't work I'm kind of struggling with what to save and what to load

so do I save SaveSystemManager.GetCurrentSaveData()
and then I deserialize it? or how do I do it exactly I'm really confused with this

so what I did is that I made a new script I called it InventorySaver and I tried to do it like this and it didn't work for me

C#:
public class InventorySaver : MonoBehaviour, ISavable
{
    public int saveSlot = 0;

    public object CaptureState()
    {
        SaveSystemManager.Save(saveSlot);

        return new InventorySaveData
        {
            saveData = SaveSystemManager.GetCurrentSaveData()
        };
    }

    public void RestoreState(object state)
    {
        var data = (InventorySaveData)state;
        if (data.saveData == null) return;
        SaveSystemManager.SetCurrentSaveData(data.saveData);
        SaveSystemManager.Load(saveSlot);
    }

    [Serializable]
    public struct InventorySaveData
    {
       public SaveData saveData;
    }
}

so if that's not the correct way please tell me what to do ?‍?️

Thank you
 
Your code looks good to me.
You say it doesn't work, but you didn't say what doesn't work. Does nothing happen or do you get an error?

Does your Inventory have an InventorySaver (the UIS one not your custom one)?
Do you have the InventorySystemManagerItemSaver component next the the UIS Save System Manager component?

Add a Debug.Log to know if your save data is allways null.
If it is not null then you can try to check the number of things it tried to save:
Code:
Debug.Log(data.saveData.SaveDataKeys.Count);
 
sorry I didn't specify what doesn't work

I get this error

Code:
SerializationException: Type 'UnityEngine.Object' in Assembly 'UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers (System.RuntimeType type) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.FormatterServices+<>c__DisplayClass9_0.<GetSerializableMembers>b__0 (System.Runtime.Serialization.MemberHolder _) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Collections.Concurrent.ConcurrentDictionary`2[TKey,TValue].GetOrAdd (TKey key, System.Func`2[T,TResult] valueFactory) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.FormatterServices.GetSerializableMembers (System.Type type, System.Runtime.Serialization.StreamingContext context) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo () (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize (System.Type objectType, System.Runtime.Serialization.ISurrogateSelector surrogateSelector, System.Runtime.Serialization.StreamingContext context, System.Runtime.Serialization.Formatters.Binary.SerObjectInfoInit serObjectInfoInit, System.Runtime.Serialization.IFormatterConverter converter, System.Runtime.Serialization.SerializationBinder binder) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize (System.Type objectType, System.Runtime.Serialization.ISurrogateSelector surrogateSelector, System.Runtime.Serialization.StreamingContext context, System.Runtime.Serialization.Formatters.Binary.SerObjectInfoInit serObjectInfoInit, System.Runtime.Serialization.IFormatterConverter converter, System.Runtime.Serialization.SerializationBinder binder) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteArray (System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo objectInfo, System.Runtime.Serialization.Formatters.Binary.NameInfo memberNameInfo, System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo memberObjectInfo) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write (System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo objectInfo, System.Runtime.Serialization.Formatters.Binary.NameInfo memberNameInfo, System.Runtime.Serialization.Formatters.Binary.NameInfo typeNameInfo) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize (System.Object graph, System.Runtime.Remoting.Messaging.Header[] inHeaders, System.Runtime.Serialization.Formatters.Binary.__BinaryWriter serWriter, System.Boolean fCheck) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph, System.Runtime.Remoting.Messaging.Header[] headers, System.Boolean fCheck) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph, System.Runtime.Remoting.Messaging.Header[] headers) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (System.IO.Stream serializationStream, System.Object graph) (at <437ba245d8404784b9fbab9b439ac908>:0)
SavingAndLoading.SaveFile (System.Object state) (at Assets/Scripts/Scripts/Savable/SavingAndLoading.cs:42)
SavingAndLoading.Save () (at Assets/Scripts/Scripts/Savable/SavingAndLoading.cs:28)

I added Inventory Saver (custom and the one from UIS) in the player and I added all the necessary Scene Setup in the "Game" gameobject

I also tried using just my Inventory Saver on its own and I still get an error so this Error is when I used both (I dont think it will change anything but I just tried not sure) but I never removed my custom InventorySaver :)

so the player has (Savable Entity, Inventory, Both Inventory Saver, and other UIS stuff for the player)
and the Game has (Save System Manager, Inventory System Manager Item Saver, and the other UIS manager stuff)

and I also save using my save system
so am I missing anything?
 
The error says that you cannot serialize UnityEngine.Objects. It happens within your own save system, not ours.
Your save system that you've made converts the "object" straight to binarry. The binarry formatter doesn't know how to deal with UnityEngine.Objects.
You'll need to improve your save system such that it can serialize UnityEngine.Objects. My advice would be to convert the save object first to JSON and then to binarry.

Something around those lines (not tested):
Code:
//Binarry formatter.
BinaryFormatter bf = new BinaryFormatter();
// Create the save file.
FileStream dataFile = File.Create(saveFilePath);
//convert the save data to json.
var dataJson = JsonUtility.ToJson(saveData);
//convert the Json to binary and write it to the file.
bf.Serialize(dataFile, dataJson);
//Close the file.
dataFile.Close();

I would recommend you do more research on Saving/Loading Serialization/Deserialization in Unity as this is not something solely related to our asset.
 
I changed the SaveFile to this
Code:
private void SaveFile(object state)
    {
        using (var stream = File.Open(SavePath, FileMode.Create))
        {
            var formatter = new BinaryFormatter();
            var dataJson = JsonUtility.ToJson(state);
            //convert the Json to binary and write it to the file.
            formatter.Serialize(stream, dataJson);
            stream.Close();
        }
    }
I dont get any error but when I load I get errors how to convert back to the original form to deserialize?
or do I use json serilization? im not sure to be honest

I looked up a bit and just tell me if I'm in the right track
thank you!
☺️
 
ok so I looked into it... and I couldn't find anything..

I'm not sure if I'm doing it correctly
I tried to look for UnityEngine.object to binary (for loading not sure) and all I can find is the same things I already did.
and I tried other things too.. so converting to json and then binary works I think for saving
but I can't load and I can't find any solution to this.
 
When you read your save data back from the save file to convert binarry to JSON before converting it back to the save data.

To save you are doing
object -> Json -> binarry

So to load obviously you need to do
binarry -> Json -> object

As mentioned before this has nothing to do with our system, that's just one way people usually save their games. Some people even add a stage for crypting/decrytping the save text.

in our own save system we read the save data from disk like so: (Note that you aren't using this function since you are using your own saving system that read/writes from/to disk)

Code:
if (!File.Exists(saveFilePath)) { return null; }

var saveData = new SaveData();

BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(saveFilePath, FileMode.Open);
JsonUtility.FromJsonOverwrite((string)bf.Deserialize(file), saveData);
file.Close();

Hopefully that helps. You should be able to find a ton of documentation, blog posts and video tutorials on this subject since most games have a save system. You even have the source code for our save system that you can read through to see how we did it.
You don't have to do the same for your game, but it's always good to see how other people do things and compare with your solution.
 
Last edited:
ok so I tried this but I'm confused with the return type

you gave me a script with no return type and mine uses Dictionary<string, object>

so if i tried to change the return type for loading from Dictionary<string, object> to just object will it work? and i think ill have to change all the other scripts to match the form and I don't think it will really work..

so is there any other way to convert back to object and still keep my current loading form?

That's the original saving and loading from the video:
Code:
    private void SaveFile(object state)
    {
        using (var stream = File.Open(SavePath, FileMode.Create))
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(stream, state);
        }
    }
    
    private Dictionary<string,object> LoadFile()
    {
        if(!File.Exists(SavePath))
        {
            return new Dictionary<string, object>();
        }
        using (FileStream stream = File.Open(SavePath, FileMode.Open))
        {
            var formatter = new BinaryFormatter();
            return(Dictionary<string,object>)formatter.Deserialize(stream);
        }
    }


I looked up for answers online for my problem but I can't really find a solution to this so I'm not sure if this method of saving and loading for UIS is the right way for me..

because I had no problem with saving before this..
I'll keep looking more into this but i just wanted to ask to see if you have a solution to this.
thank you?
 
I would recommend you try ti understand why you are using a dictionary, what's the key and what's the value?
From my understanding the key is a string that points to a ISavable component and the value of type object is that save data for that component. Is that how you understand it?

Also you need to make sure that whatever type you a serializing with the binarry serializer has to be the exact same type that you are deserializing.

For example in the your code it would seem you are serializing a dictionary and therefore when you deserialize it needs to be of type dictionary.
In my code I serialize a string and then deserialize a string. And the string is a JSON representation of my save data.

So in my opinion you have two choices either:
1) convert your entire dictionary to JSON, serialize a string, deserialize a string, convert the JSON back to a dictionary
2) Convert the UIS save data to JSON before returning it when Capturing data and then convert JSON to save data when Restoring data.

I hope that make sense. Make sure to understand the code you are using, if you blindly copy paste code from a tutorial you won't learn as well as if you question why the tutor designed his code that way.
 
ok so I tried working on this.. and it still doesn't work.. I'm not sure if I'm doing it right or not I tried a lot of things but I always get errors

so can you tell me if this is correct or not for the first option you recommended?

C#:
    private void SaveFile(object state)
    {
        var formatter = new BinaryFormatter();
        var stream = File.Open(SavePath, FileMode.Create);
        var toJson = JsonUtility.ToJson(state);
        formatter.Serialize(stream, toJson);
    }
    private Dictionary<string, object> LoadFile()
    {
        if (!File.Exists(SavePath))
        {
            return new Dictionary<string, object>();
        }

        var formatter = new BinaryFormatter();
        object obj = new object();
        FileStream stream = File.Open(SavePath, FileMode.Open);
        JsonUtility.FromJsonOverwrite((string)formatter.Deserialize(stream), obj);
        return (Dictionary<string, object>)obj;
    }

I also tried using this on loading

C#:
    private Dictionary<string, object> LoadFile()
    {
        if (!File.Exists(SavePath))
        {
            return new Dictionary<string, object>();
        }

        var formatter = new BinaryFormatter();
        FileStream stream = File.Open(SavePath, FileMode.Open);
        return (Dictionary<string, object>)JsonConvert.DeserializeObject((string)formatter.Deserialize(stream));
    }


the Error I get is
Code:
InvalidCastException: Specified cast is not valid.
SavingAndLoading.LoadFile () (at Assets/Scripts/Scripts/Savable/SavingAndLoading.cs:54)
SavingAndLoading.Load () (at Assets/Scripts/Scripts/Savable/SavingAndLoading.cs:34)


so what I'm trying to do is I'm converting the object to json and then I serialize it to binary

and on loading I deserialize the binary to json and then back to object and then I cast it to dictionary?

tried other things too but I always get the same result..
 
The error says that the object you are deserializing cannot be cast to a dictionary.

What type is the object you are serializing? You can check by adding a Debug.Log (state.GetType()). After deserializing you must cast it back to whatever object type it was originally.

Btw do you know why you are using a dictionary? From your explanation it is not clear you understand what your dictionary is used for. What are the keys and what are the values in the dictionary. Once you can answer that the solution should be clear.

I would like to point out once again that your issue has nothing to do with the Ultimate Inventory System. For generic C#/programming questions like that you should rather ask them in the Unity forums or Stack Overflow.
 
Top