Ever since the well-known talk by Richard Fine about ScriptableObjects (link below) they’ve become more and more prevalent in Unity system architecture. Ryan Hipple took it a step further in his own talk (link below) and gave us a ton of practical examples on how to use them.
Despite both of these AND the Unity documentation on ScriptableObjects (link below) I’ve found that this powerful tool still has a bunch of gotchas. So I’d like to clear them up by talking about EXACTLY how they behave and why.
Here’s everything I know about them and how they work. If you can fill in a ‘???’ or provide additional insight into any of these points, please comment below. Note that all tests to verify these behaviours were done in Unity 2018.1.0f2 on a Windows OS.
ScriptableObjects - Their Callbacks And When They Receive Them
Awake() - From docs: This function is called when the ScriptableObject script is started.
Specifically, there are 3 cases in which a ScriptableObject receives an Awake() message from Unity:
1 - When the ScriptableObject is created (in editor or at runtime)
2 - When the ScriptableObject is selected from the project window in the Editor (??? what’s the root cause of this? Inspector?)
3 - When a scene is loaded IF at least one MonoBehaviour in that scene is referencing the ScriptableObject asset
Important! ScriptableObjects will only receive Awake() if OnDisable() was previously called on the object. So if Awake() was called because of case 2 and case 3 occurs before other callbacks, it won’t receive Awake() again.
OnEnable() - From docs: This function is called when the object is loaded.
Specifically, there are 3 cases in which a ScriptableObject receives an OnEnable() message from Unity:
1 - Immediately after the ScriptableObject’s Awake() (before other callbacks on this or other objects)
2 - When the Unity Editor reloads IF in a scene that has a MonoBehaviour referencing that ScriptableObject asset (right after OnDisable())
3 - When entering play mode IF in a scene that has a MonoBehaviour referencing that ScriptableObject asset (right after OnDisable())
OnDisable() - From docs: This function is called when the scriptable object goes out of scope.
Specifically, there are 4 cases in which a ScriptableObject receives an OnDisable() message from Unity:
1 - When a scene is loaded and there are no MonoBehaviours in that scene that reference the ScriptableObject asset
2 - When the Unity Editor reloads IF in a scene that has a MonoBehaviour referencing that ScriptableObject
3 - When entering play mode IF in a scene that has a MonoBehaviour referencing that ScriptableObject
4 - Right before any OnDestroyed() callback
Important! OnDisable() will only be called if OnEnable() was previously called on the ScriptableObject.
OnDestroy() - From docs: This function is called when the scriptable object will be destroyed.
As far as I can tell, that’s completely accurate. What’s worth mentioning here are the 3 (???) causes for ScriptableObject to be destroyed in the first place:
1 - The ScriptableObject is deleted in code
2 - The ScriptableObject is deleted from the assets folder in the Editor
3 - The ScriptableObject was created at runtime and the application is quitting (or exiting play mode)
ScriptableObjects - Other Behaviour
ScriptableObject vs. MonoBehaviour callbacks
It’s important to note that, when a scene is loaded, a ScriptableObject will normally receive its Awake() and OnEnable() messages before any MonoBehaviours receive their Awake() or any other callbacks, no matter the Script Execution Order. (??? anyone know why that is?)
Execution Order
ScriptableObjects themselves do not follow the Script Execution Order. If called at the same time, the order in which ScriptableObjects receive Awake() is unclear. (??? what determines the order of execution of ScriptableObjects?)
Runtime Creation
When creating a ScriptableObject at runtime, it does not appear in the Project’s Assets folder, nor does it appear in the Hierarchy. (??? is there a way to view them in Editor?) ScriptableObjects created this way will get deleted either when the application quits or when exiting play mode.
Scene References
If you have a ScriptableObject that is meant to hold references to scene objects, be aware that the object fields for those references will display:
(type mismatch)
The reference will still point to the correct scene object if clicked on in the inspector. This is because, by default, object fields in assets can only contain references to other non-scene assets. This can be overridden by writing a custom property drawer for the referenced behaviour, or a custom inspector for the ScriptableObject. Even if that is done, scene objects still cannot be dragged and dropped into object field on ScriptableObjects.
Deletion
When deleting a ScriptableObject, note that other components still have the C# reference to them
Please comment below if you have anything constructive to add.
Resources
Richard Fine panel/talk:
Ryan Hipple panel/talk:
Unity Documentation for Scriptable Objects:
Unity Documentation for custom inspectors and property drawers: