When trying to initialize my scriptable objects I get null pointers, because the objects I want to reference didn’t run through Awake() yet.
I already found this post, where the scriptable object Awake() confusion gets discussed.
But at this point I don’t know what to do.
Awake() in my ScriptableObject is never called, and OnEnable is also called in the editor, and before all other awakes. I need something like Awake, otherwise I can’t hook into my other classes instanced events!
Unfortunately ScriptableObjects don’t implement all of the MonoBehavior / Component lifecycle events.
ALSO, the lifecycle of ScriptableObjects is considerably different: once accessed (in any way, code, editor, etc.) they exist forever, until you either delete them in the editor, or close the editor.
If you need runtime lifecycle control, wrap them in a MonoBehavior and make a prefab. Mucky but it will absolutely give you explicit control, and by making the MonoBehavior a singleton, you should be able to achieve precisely what you need.
ALSO: if you have a private field in a ScriptableObject, it WILL NOT RESET each run. I.e., if you make a bool Initialized;, then once true it will stay true until you leave the editor. Huge potential source of confusion.
To fix that, add a [System.NonSerialized] decorator to privates that you really want restored to default each run.
Oh boys, thank you for the hints! The fact that data in SO’s persist through editor sessions but not through game sessions is also very confusing.
I implemented my “Init” call the following way:
– My GameManager is a Singleton MonoBehaviour
– The GameManager holds a public static Action awake
– In my GameManagers Awake() method I call awake?.Invoke()
– The static action makes it possible for my Scriptable objects to subscribe OnEnable: private void OnEnable() => GameManager.awake += OnAwake;
Have fun with this mess, whoever finds this ¯_(ツ)_/¯
Hit like and subscribe, ring the bell, dunno.
Our approach was to have our ScriptableObjects implement two different interfaces, an ISOInitializable and ISOAsyncInitializable call… then we just put them in two lists in the startup scene script. We installed them in the order in which they need to be tickled, and pass them on to a tickler that drives all the calls, either single-call or via a coroutine (if async).