Monday, March 16, 2020

Serialization of Scriptable Objects in Unity (Nesting)

Whilst continuing with the tutorials from Mister Taft I came across an odd situation whereby his tutorial didn't actually work.

<This is the tutorial in question>

I can only assume that Unity has changed the way they work with SO's and serialization as no matter how hard I tried to follow, I couldn't replicate the saving of the SO's.

What follow's is how I worked around it to make a working saveable inventory that works along with Mister Taft's series. This may be useful to you as a general concept, but will be of most use to students of Mister Taft!

I will be using Total JSON to convert my serializable list into a nice pretty JSON string that we can save out, available on the asset store for free. The built-in JSON utilities will NOT work for this as it doesn't like more complex data. Total JSON is pretty lightweight so the tradeoff for me is negligible, you could also do this with a binary formatter but it's not something I'll be covering.

I haven't put much if any error checking into this as I wanted the concepts to be clear, you SHOULD be adding your own!

If this helps anyone, let me know! :)

A big thank you to Ashfid on the Mister Taft Discord for giving me the idea on how to solve this issue!

You can download the whole InventorySaver.cs file below and refer to my GitHub for other scripts if required. Come to the Mister Taft discord server if you have any questions!


Download the complete InventorySaver.cs file


Item Database

I'm going to be utilising the idea of not actually saving a scriptable object at all, I don't think Unity wants you to either! Instead what I did was save a reference and recreate the inventory when loading!

To that end what we first have to start with is an item database, this in itself is a simple scriptable object with a single method built-in.


Create this Scriptable Object in Unity and drag over the Red & Green Potion SO's so they appear as elements in the Items list.



We are going to use this database to recreate the player inventory when we load the save file!


The Serializable Class 

or, where I'm planning on storing data before and after serialization!

As mentioned I'm not actually going to store the Scriptable Objects, I'm going to create a snapshot if you will of what is in the player inventory when we want to save and I'm going to recreate the inventory at load time from that snapshot!

To visualise imagine a hand of cards from a normal deck, if I wanted to record an inventory my hand mid way through a game to recreate later I might write down "7 of clubs, 3 of hearts, 4 of diamonds". I'm not storing the actual cards anywhere, but when I come back later I can pick the recorded cards back out of the deck to recreate my hand.

I'm going to do exactly the same with this list, additionally I'm going to store the count as well so we can have multiple of the same item!




Putting it all together, the InventorySaver class.

The inventory saver is where the real work gets done. It needs a reference to the PlayerInventory and The Item Database we created earlier.

It needs to be attached to the GameSaveManager created during Mister Tafts earlier tutorial.



Saving:

for Saving there will be 3 steps and all of them will occur on the OnDisable() call from the script.



BuildSaveData() is performing the snapshot discussed earlier.

and finally writing the file to disk


I have used the pretty string format option just for ease of reading the saved files, speaking of which when you exit out and have any of the potions in your inventory you should end up with a json file in your persistant data folder similar to the following.



Loading:

Loading is almost a reverse of saving of course but there are 4 steps this time. We additionally want to clear the playerInventory before we recreate from the snapshot.

For my example I will be performing the loading in the OnEnable() method.


for the LoadScritables() we are taking the text json from disk and placing it into our JSON object. then deserializing into our snapshot.


with the snapshot in memory, it's time to recreate the inventory and this is where our Item Database really comes into play!




14 comments:

  1. I don't get the SL.serializableList.Clear(); line.
    What is SL? i can't find it.

    ReplyDelete
    Replies
    1. Hi Zero, you need to declare SL in the InventorySaver.cs script, have a look on GitHub (link above) if you want to see the whole class.


      //SL is our serializable class that contains a representation of the items we want to save - this is a COPY

      private SerializableListString SL = new SerializableListString();

      Delete
  2. now makes sense! thank you very much sir! I'll let you know the results of this! You've been of great help :)

    ReplyDelete
    Replies
    1. You are welcome, pop in to the discord channel if you want, friendly bunch in there! :) Also thanks for being my first commenter! :D

      Delete
  3. Thanks a lot for this, James, really helpful! What would your method be for creating a 'clear inventory save data' button function? Clear player inventory, clear the serializable list, delete newsave.json?

    ReplyDelete
    Replies
    1. you are welcome MoodGrim, happy to have helped.

      I'd make a function something like below in the InventorySaver class.

      public void SaveReset()
      {
      myInventory.myInventory.Clear();
      SL.serializableList.Clear();
      BuildSaveData();
      SaveScriptables();
      }

      that would clear out and write an empty save, haven't tested that it's off the top of my head, but should work. Pop over into the discord channel if it doesn't :)

      Delete
    2. Thanks! Seems to work fine. My one modification is zeroing out the numberHeld in the inventory items. Otherwise that number will keep increasing across resets, I think.

      foreach (InventoryItem invItem in myInventory.myInventory)
      {
      invItem.numberHeld = 0;
      }

      Delete
  4. Replies
    1. :) aww, thank you! Happy to have helped :) Comments like this make my day, so thanks for taking the time to leave one!

      Delete
  5. Where do you get the JSON library. My JSON jsonObject isn't working. Do I need something from the asset store?

    ReplyDelete
    Replies
    1. Hi yes I did mention it above, but I should have been more explicit. You need TotalJSON from the asset store. It's free. :)

      Delete
    2. Oh whoops. I skipped over that part. Well thankyou for this great tutorial. :)

      Delete
    3. You are very welcome, hope it's been useful!

      Delete
  6. this fixed my problem with type mismatch but my inventory still resets between scenes :(

    ReplyDelete

ASP.NET Core(porate) , yup I'm hilarious :|

 So, lately, I have been in full corporate mode. That means looking after my team of engineers and when I have some gaps in my calendar work...

Most Viewed