2

In the following script how can I get the path to the script in the Assets folder?

using UnityEngine;
using System.Reflection;
using System.IO;
using UnityEditor;

[InitializeOnLoad]
public class MyWindow : ScriptableObject
{
    static string pathToScript;

    [MenuItem("Window/My Window")]
    static void Open()
    {
        // Do something with `pathToScript`
    }

    // This function is NOT called when the object is loaded.
    protected void OnEnable()
    {
        var script = MonoScript.FromScriptableObject( this );
        pathToScript = AssetDatabase.GetAssetPath( script );
    }
}

The problem is that OnEnabled it's not called, also it seems the only way to get a path to the script is through AssetDatabase.GetAssetPath which requires an instance.

The version in Unity is 5.5.0b3.

3 Answers 3

1

For the OnEnable() function to be called while inheriting from ScriptableObject, you must call the CreateInstance() function from ScriptableObject class.

[InitializeOnLoad]
public class MyWindow : ScriptableObject
{
    static string pathToScript;
    static MyWindow windowInstance;

    [MenuItem("Window/My Window")]
    static void Open()
    {
        Debug.Log("Open:" + pathToScript);

        //Do something with `pathToScript`

        if (windowInstance == null)
            windowInstance = ScriptableObject.CreateInstance<MyWindow>();

    }

    protected void OnEnable()
    {
        Debug.Log("Enabled!");
        var script = MonoScript.FromScriptableObject(this);
        pathToScript = AssetDatabase.GetAssetPath(script);
    }
}

Another way to use ScriptableObject.CreateInstance is to call it from another script.

[InitializeOnLoad]
public class MyWindow : ScriptableObject
{
    static string pathToScript;

    [MenuItem("Window/My Window")]
    static void Open()
    {
        Debug.Log("Open:" + pathToScript);

        //Do something with `pathToScript`
    }

    protected void OnEnable()
    {
        Debug.Log("Enabled!");
        var script = MonoScript.FromScriptableObject(this);
        pathToScript = AssetDatabase.GetAssetPath(script);
    }
}

Test:

public class Test : MonoBehaviour
{
    public MyWindow myWindow;
    public void OnEnable()
    {
        if (myWindow == null)
            myWindow = Object.FindObjectOfType<MyWindow>();

        if (myWindow == null)
            myWindow = ScriptableObject.CreateInstance<MyWindow>();
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

In my experience, all "event methods" (OnEnable, Awake, ...) on Scriptable Objects (SO) are only called, when an instance of an SO is referenced by an active scene component. Just creating an instance is not enough. This is contrary to claims that "SOs live outside the scene".

It is particularly confusing in the editor. One can e.g. create an instance of an SO in the project's assets and it will look like OnEnable, Awake, ... is being called as you'd expect it. But as soon as you're building and running the game outside the editor, these methods will not be called whatsoever anymore if the SO instance wasn't assigned to an active scene object or loaded by a script in another way, but just exists in the assets.

Furthermore I couldn't find out when the editor decides to load or re-load an SO instance that only exists in the assets. This has been very inconsistent and unreliable in my experience.

I've read that there were more bugs causing these methods to be emitted with SOs prior to the 2018 versions of Unity and that it's more reliable now, but documentation on SOs is still inaccurate and misleading today.

1 Comment

the SOs that are not referenced by any script from any scene will be discarded in build. This is for stripping reasons, Unity doesn't include assets that are not part of the big dependency graph of objects referencing to each other. To work around that, you can use Resources folder so that the SO won't be discarded in build. This actually makes sense really
0

As @Vinz mentioned, an SO must meet one of the following to be activated:

  1. Be referenced by a MonoBehaviour, or another SO which is.
  2. Be inspected in the editor

Regarding the second option, it seems that when inspected once they are considered active for that unity session, but this is very inconsistent.

My solution is to have a MonoBehaviour whose sole job is to reference the Scriptable Objects. When this is in the scene and referencing your SOs, they will be consistently called in edit mode and for builds.

using UnityEngine;


public class ScriptableObjectActivator : MonoBehaviour
{
  public ScriptableObject[] scriptableObjects;
  void Awake() => DontDestroyOnLoad(gameObject);
}

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.