Can I serialize Scriptable Objects references reliably?

I have a fairly sophisticated saving/load system in my game. I am saving Serializable classes that hold a reference to a Scriptable object. A typical entry for one of these looks like this in the Json file:

{
            "HasIngredientBeenUnlocked": false,
            "WherePurchased": 7,
            "Item": {
                "instanceID": 19898
            },
            "Quantity": 5,
            "NewCounter": 3
        }

You will notice under the category “Item” there is a InstanceID: 19898. This is a serialized reference to a scriptable object.

It seems like this instanceID changes from time to time.

If this is changing, its a disaster in my saving/loading system. Because this invalidates the reference in the save data. Does anyone have any insights into this?

5 Likes

Yeah, instance ID’s/GUID’s are only reliable if you save everything all at once and have a way reference the items by GUID. You can either
A) simultaneously serialize your items into a Dictionary or file structure using their GUID’s as a name/key, or
B) make your own lookup system (probably using the item name as your lookup - Resources folder is handy in this).

2 Likes

So rather than using the instanceID, use a string (a unique name–probably what the scriptable object is called) and do a lookup at load time and swap it out with a reference to the scriptable object when its in memory?

And your thinking put the scriptable objects in the Resource folder so I can find them by name.

3 Likes

Yeah, pretty much.

4 Likes

What version of Unity are you using? Unity 5.5.0b4 has this change you may want to be aware of:

Backwards Compatibility Breaking Changes

  • JSONUtility: EditorJsonUtility now serializes object references by assetGUID/fileID rather than InstanceID, making the serialized data stable across Editor sessions.
2 Likes

I’m using Unity 5.5.2. I’m not doing anything special to save the objects with the scriptable object references.

I make a super object that is just a container of all the save data and use this code to write it out:

void SaveFranchiseBlobJson()
        {
            FranchiseAgenda agenda = World.instance.Franchise;
            string path = FranchiseBlobPathJson (agenda);
            string js = JsonUtility.ToJson (Data, true);
            File.WriteAllText (path, js);
        }

It is the JsonUtility that is calling the shots on saving the scriptableObject reference as a InstanceID. I think what I’m going to do is have a string in each of my objects that will contain the name of the ScriptableObject that is referenced–as well as the reference to the scriptable object. On loading, I will simply look up the ScriptableObject reference and replace the reference in the object to whatever is currently in bundle. It will all appear seamless to the current architecture.

This seems to be working fine when calling ToJson() in the built player. However when I first serialized my data in a json file from the Editor (play in editor) - I got “instanceID” saved instead of m_FileID and m_PathID. Loading this file from a built game resulted in a null references (haven’t tried another Editor session but my guess it won’t work either).

I wonder if references saved in one build of the game will remain valid in another build (a newer version)? AFAIK the only way to permanently reference a certain asset is by GUID (as long as .meta file exists). I looked at m_FileID numbers in saved json and tried to compare them with anything in my SO file to no avail. It seems to be those fileIDs are temporary just as InstanceID but persist in a certain build of the game.

So I guess it’s still a good idea to try and avoid having SO references in my models at all.

3 Likes

Did you find any solution to this? My saved data persisted among several builds, but in the last one, m_FileIDs changed so it wont work anymore. Is there any way to change m_FileIDs for GUIDs in Jsonutility serialization? I find very convenient how jsonutility.fromJsonOverwrite only writes the variables that match with the current class definition, i.e. your loading system wont brake even if the class definition of the object you want to deserialize has changed a bit. So, im looking for a solution with that feature and with GUIDs as well, thanks in advance

I’m also curious about the current state of this.

I’m serializing (via JSONUtility) some objects that have references to ScriptableObjects in them.

Those references are saved as “InstanceID:”.

So far, they have been reliable, but after reading about this I’m not so sure if that’s really the case.

If you want to be 100% confident in your save system it’s best to take over the reigns as much as possible. Which means giving any SO’s that you expect to save some form of unique identifier, and creating serialisable surrogates for any classes that contain references to SO’s so that it saves the identifier rather than the Unity reference. Then on loading you us the identifier to look up from a database to restore the runtime data.

Overall it’s more work, but that’s the price you pay for reliability.

3 Likes