Unity Serialization… behind the mistery of ScriptableObject

Unity serialization is a powerful feature but a little bit hard to master. Working on my AudioLogic plugin (soon on the AssetStore) I start fighting against the serialization in Unity. I need it because when you modify your audio library from the custom window you will expect that after re-open unity or after hit play your changes are still there and not vanished as a dust in the air… Soooo, I started toying around serialization stuff in Unity.

This will be more a memorandum for me to remember what I must do and what NOT to serialize a complex structure correctly (like MultiList, or multi array).

First of all: what the hell are ScriptableObject and why I need it to serialize my data?

There are billions way to serialize data not related to ScriptableObject, but to take advantage of the optimization in size and performance that Unity give us we must know a little bit about ScriptableObject.
In a short terms ScriptableObjects are a “Monobehaviour” version for data structure, this it means that it react at some MonoBehaviour/Unity callbacks like OnEnable, OnDisable and OnDestroy. As MonoBehaviour you couldn’t use the new constructor but use CreateInstance instead.
If you ever worked with serialization in unity you know that to serialize you could use:

This will tells to Unity that you want to serialize the Foo class with its private field privateVal. Well, that’s all. Bye!

For my “simple” mind this was all I need to know about serialization I though, but what if the structure will be more complex?

See the following structure:

Now fill the ListOfBase list with some instances of Base class. If you modify the name in an instance of BaseClass the changes will be reflected in the corresponding element of the list.
That’s what we want.
Now Play and stop the editor. As you could see your changes done before are maintained in the Base instance and also in the list. Wow, we did it! Serialization is done again!
Well, now try to modify again the same Base instance. What? Why do the changes are not reflecting in the list?
Unity correctly serialize your data the first time you enter the play, but when you exit from play mode, the reference of Base class stored in the list are gone and now we have ListOfBase decoupled from its Base instances and this is why the changes on Base are not being reflected in the list.
It’s the time for our wonderful magic ScriptableObjects to comes in.
As I mentioned before, ScriptableObject are similar to MonoBehaviour, thinking to a component attached to a GameObject; all the public variable stored in a component are serialized and when you come back from play mode the value on the GameObject are the same as before playing. Also the Array data are serialized correctly, so it means that Mono will maintain the correct reference to the instances of the objects after de-serialization.
ScriptableObject work in the same manner. So let’s try to change our structure in this way:

Do the same changes we did before and see what happen. No more decoupling error and we are all happy. Go ScriptableObject GO! Yuuuhuuu! Then close your project go out get drunk and drink in the name of Serialization. The next morning you open the project and your saved data are gone… again.
That’s because ScriptableObject maintain the reference but just for the current editor session.
One difference between MonoBehaviour and ScriptableObject are that monobehaviour will “live” and alive in the scene and the scene of course will survive in the project and between editor session, the ScriptableObject not.
You must tell to the project that your ScriptableObject contains important data on it and needs to survive between session but there’s no easy way to do it.
The only one I found is to create a .asset file, add the ScriptableObject on it (similar to what we do with a Component for a GameObject) and add it to the AssetDatabase. In this way the project known that there’s an asset to take care of. Below the method to create and add your scriptableObject to the project:

Phiuw! We did it! Really, this time we did it! Go out and get drunk again in the name of ScriptableObject… next morning you open your project and… you will find your data and asset correctly stored! :D

In addition I want mention that if your structure is more complex (like multi arrays) then the one I shown , you could use subAsset to achieve that.
If you have a List in a List like:

You will encounter some issues. I don’t know if it is again a decoupling issue or something else, but you could check that something is going wrong looking at the .asset file inspector. After creating the Container and the .asset, you will see in the inspector a TypeMistmatch appear in the property field for listOfBase.
I think it happens because the root of the data structure must be a ScriptableObject, but what live inside the ScriptableObject mustn’t be a ScriptableObject itself. I’m not 100% sure of it, but a workaround to solve this problem with decoupling/nested ScriptableObject is using subAsset.
Remember the similarity with the monobehaviour component, so also in this case we could add a subAsset to an asset like a new component on the GameObject.
In this case we change the Base class to ScriptableObject and add the instances as subAsset on the Container asset. Remove the ScriptableObject from ListOfBase and leave it as a simple class do the trick.
So here’s all the code:

 

Now if you want to check if it work, modify a Base instance in the listOfBase and it will reflect also in the multiList list. the data now are stored and serialized correctly also between session.
One last thing before going to get drunk in the name of the SubAsset, if you want to hide to the user the data and the subasset stored in the .asset you could set the hideFlag. Pay attention that if you set

Unity will not be able to create the .asset file and throw a error in the console.
The hideFlags tells to Unity what must be shown in Hierarchy, in Inspector and what to Save or not. So be worried about it and see the Unity documentation.

Phiuuuuw… That’s really really really all. I hope it could be useful for someone, as I told at the beginning this is a way to stick what I’ve learned in my mind and it’s not supposed to be the “perfect” post on Unity serialization, so if any of you has something to add or correct, throw a comment here! :)

Share this:
Facebook Twitter Plusone Email

10 Comments

  • what would you recommend doing to update the saved asset.
    I want to save out my scriptable objects, with subAssets, modify the assets, and re-save the asset wit additional subAassets.
    Would i need to recreate the asset with the new subAssets, or can i just add the additional subAssets the the mainAsset then use AssetDatabse.SaveAssets to save the changes as i go?

    Also if i wanted to load this data can i just load the mainAsset, asuming that it will have the pointer to its subAssets, or do i need to load them all and handle each scriptableObject?

    • Once you create the main asset you can add every subasset you want with:
      AssetDatabase.AddObjectToAsset (subAsset, container);
      Then you need to write the changes to disk through AssetDatabase.SaveAssets() or if it gives you some problem try to reimport the asset AssetDatabase.ImportAsset to have the changes before saving the project.
      In my case you have references to all ScriptableObject subasset in the listOfBase of Container, so once you load the main asset you get all the other ScriptableObject reference. But if you want to load all the assets attached to the main asset try to use AssetDatabase.LoadAllAssetsAtPath(mainAssetPath), it will return an array of Objects.

  • I am trying to follow what you did ( I am in the process of trying to understand how serialization works in unity). But right off the bat I don’t understand what you mean by: “Now fill the ListOfBase list with some instances of Base class” – what have you done with the 2 classes you wrote?
    I tried reproducing the first problem you describe as decoupling. I made this script with your classes:

    [System.Serializable]
    public class ListOfBase : MonoBehaviour
    {

    [System.Serializable]
    public class Base
    {
    [SerializeField]
    private int privateVal;
    public string name;
    }

    [SerializeField]
    private List list;
    }

    But i had to make ListOfBase inherit from monobehaviour in order to attach it to a game object.
    Then I set the list size to 2…changed the values…played ….stop playing, closed unity and opened unity…and the entries in the list remained intact…

    So obviously you are describing a different situation…I am unclear on how to reproduce the problem you describe. I am really interested in understanding this subject…so any clarification would be greatly appreciated.

    • “One difference between MonoBehaviour and ScriptableObject are that monobehaviour will “live” and alive in the scene and the scene of course will survive in the project and between editor session, the ScriptableObject not.
      You must tell to the project that your ScriptableObject contains important data on it and needs to survive between session but there’s no easy way to do it.”

      If you attach a monobehaviour to a gameobject in the scene it will serialize by default, so you will not encounter the problem I mentioned.

      The problem is related mainly to editor scripting. Try to define ListOfBase as a scriptable object, so you cannot attach it on a GameObject, than move Base class outside from ListOfBase and fill the list with some Base instances. Create a simple EditorWindow script that show the data stored in the ListOfBase. Et voilà the issue is served… :)

  • so, how do you load the assetdatabase “container” if it already exists with your ListOfBase, let’s say after restarting unity.. or opening a different project. You don’t want to have to recreate the entire ListOfBase work again….

    seems to me, that Unity does all that above ok.. until you switch projects or close/open unity again and you try to LoadAllAssetsAtPath on it… it just returns a sterile UnityEngine.Object.

    • I’m not sure I have fully understood your question, but what I can tell is that if you create a simple custom editor window to test the container class, you will see that every time you load the container in the window you will get all the reference to ListOfBase as expected.
      Did you try to load only the container with :
      Container obj = (Container) AssetDatabase.LoadAssetAtPath (path, typeof( Container ) ); ?

  • hi, thanks
    do you have this ‘problem’:
    http://forum.unity3d.com/threads/assetdatabase-addobjecttoasset-doesnt-seem-to-work-with-scriptableobjects.183342/#post-1252942
    where unity creates am empty object and puts your objects under it?
    thanks

  • Hey, I was just wondering how you even attached the ScriptableObjects to anything that shows up in the inspector. Did you make them be components of monobehaviors? because when I make classes that derive from ScriptableObject serializable and make them serialized components of monobehaviors, their serialized fields don’t show up in the editor.

    • The scriptableObject cannot be attached to a GameObject as a component, so it doesn’t show up in a normal Inspector. You need to create a custom editor or window if you need it.

  • I’d like to inform readers of this post that i’ve created a little “factory” editor extension that allows creating ScriptableObjects directly from the editor. Check it out and let me know if that helps anyone :)

    http://www.tallior.com/2014/08/06/unity-scriptableobject-factory/

    It’s also available with the full source code

Leave a comment