(Solved, but unhappy)ScriptableObject.Awake never execute

anyone want make a test for my discussion ?

I’m open to suggestions, solutions, whatever.
This is giving me a nervous tic, everytime I have to call this Init function.

You can use [InitializeOnLoad] in tandem with OnEnable:

[InitializeOnLoad]
public class Test : ScriptableObject {

     void OnEnable () {
           Debug.Log ("Unity editor has just started up!");
     }

}

Now the method is called whenever the ScriptableObject is created and whenever Unity is launched.

8 Likes

I’m running into this frustrating issue again. Can anyone please give a TLDR of why on earth they don’t run Awake and OnEnable when everyone expects them to? (When the same methods are executed on GameObject instances)

This issue is so old.
The solution is to create your own Awake event. The Awake on ScriptableObjects happen only when it is loaded into scene and not when it is playing for the first time.

I don’t know if this is still this way now a days, but if it is, then use a boolean variable to tell when it is the first time your script is running. If its the first time, then you invoke your own “Awake” event. This will solve your issue.
Hope it helps.

I use something like this:

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

    [InitializeOnLoad]
    public abstract class ManagedObject : ScriptableObject
    {
        abstract protected void OnBegin();
        abstract protected void OnEnd();

#if UNITY_EDITOR
        protected void OnEnable()
        {
            EditorApplication.playModeStateChanged += OnPlayStateChange;
        }

        protected void OnDisable()
        {
            EditorApplication.playModeStateChanged -= OnPlayStateChange;
        }

        void OnPlayStateChange(PlayModeStateChange state)
        {
            if(state == PlayModeStateChange.EnteredPlayMode)
            {
                OnBegin();
            }
            else if(state == PlayModeStateChange.ExitingPlayMode)
            {
                OnEnd();
            }
        }
#else
        protected void OnEnable()
        {
            OnBegin();
        }

        protected void OnDisable()
        {
            OnEnd();
        }
#endif
    }

And when you inherit from it, you implement the OnBegin and OnEnd methods. It runs on starting unity editor, on entering/exiting playmode and on starting your built application.

15 Likes

Guys, I’ve found the solution - it works exactly as I need it for my SO-BulletPool:

private void OnEnable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
InitializePool();
}

public void InitializePool()
{
Debug.Log("Initialize Pool");
// do stuff
}
}
3 Likes

By the way, I was talking about OnEnable and Awake being executed when Play mode is activated. Since they don’t execute it makes OnEnable and Awake completely unusable because they don’t work during testing. So there is no good solution for that as far as I remember.

Looks like they’ve closed the ticket without discussing that Awake should be called when object is not instantiated from scripts. Has anyone created something else, or brought this to devs attention?

Unbelievable!

1 Like

The developers don’t give a shit. Bugs have existed for years and nobody care. They are only have time to develop a new features for promotion…

4 Likes

I just added an interface (lets call it: IInitializable) to the scriptableobjects requiring a special initialization that I call from a gameobject in my scene when needed.

Edit: The problem I see with this approach is that you lose the Lazy Initialization factor.

Loosely inspired by what @ wrote above.

Here’s a simple check for OnEnable to run only in editor. It uses isPlayingOrWillChangePlaymode. I use it to call a function that’s also called by Awake, so either way it runs on start (OnEnable in Editor, Awake in build)

using UnityEngine;

public class myScriptableObject:ScriptableObject {
   
    private void OnEnable() {
        #if UNITY_EDITOR
        // use platform dependent compilation so it only exists in editor, otherwise it'll break the build 
        if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
            myStartFunction ();
        #endif
    }
   
    private void Awake() {
        myStartFunction ();
    }
}

The other solutions here are good, but this is just a lot quicker - no need to make custom interfaces or listen to events or whatever. Hope it helps!

13 Likes

Documentation says “Awake is called as the ScriptableObject script starts.” but no one told you that SO script starts when you hit Play in the editor :slight_smile:

Scriptable objects are instantiated in edit mode (as all other referenced assets) and not re-instantiated or reloaded when you start playing in the editor, this allows fast start without loading everything every time.

So documentation is true, while your expectations are false. Sorry.

P.S. I was blocked by this feature too and I do understand your pain.

Seems like this solution doesn’t work if experimental enter play mode is enabled :confused:

I came across this problem as well, and I was in a situation where I needed some data to basically be calculated from the static data in the SerializeableObject. like @bobbaluba I was experiencing problems with experimental play mode.

In production, I just needed things to be calculated once, and that’s fine. In the editor I needed some data to be recalculated every play.

Here is what I worked out:

public class DataBagWithCalculatedFields : ScriptableObject
{
    private string calculatedFieldNeedsResetOnPlay;

    private void Awake()
    {
        InitialiationMethods();
    }

    private void OnEnable()
    {
        EditorApplication.playModeStateChanged += PlayStateChanged;
        InitialiationMethods();
    }

    void PlayStateChanged(PlayModeStateChange state)
    {
        if (state == PlayModeStateChange.EnteredPlayMode)
        {
            InitialiationMethods();
        }
    }

    void InitialiationMethods()
    {
        calculatedFieldNeedsResetOnPlay = "do work here.";
    }
}

Edit: Looks like this is very similar to @ 's approach it seems

2 Likes

Scumbag Dev: “Everyone misunderstands my documentation => everyone has a problem”

8 Likes

Yes, this is an issue. I need an event that triggers when a new instance of an SO is created via the editor menu so I can assign it a unique Id.
All of the other methods to do this via OnValidate, OnBeforeSerialise all feel hacky and don’t work well or execute more than needed (which is once in the whole lifecycle of the SO)

As someone just starting out with scriptable obejcts, I have to agree:
Calling this method “OnCreate” instead of Awake or at very least make it way more obvious in the documentation that we should not assume its Awake to act like for regular Gameobjects (called upon pressing the start button) would have avoided some confusion.
I do understand now why that assumption is wrong, but it’s just counter intuitive on first sight.

No, you only read half the paragraph. The other half says:

“This happens as the game is launched and is similar to MonoBehavior.Awake.”

But it does NOT happen ‘as the game is launched’ – as has been extensively documented in this thread.

2 Likes