简体   繁体   English

未调用 ScriptableObject 的 OnEnable 函数

[英]OnEnable function from of ScriptableObject not being called

In the following script how can I get the path to the script in the Assets folder?在以下脚本中,如何获取 Assets 文件夹中脚本的路径?

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.问题是OnEnabled它没有被调用,而且似乎获得脚本路径的唯一方法是通过需要实例的AssetDatabase.GetAssetPath

The version in Unity is 5.5.0b3. Unity 中的版本是 5.5.0b3。

For the OnEnable() function to be called while inheriting from ScriptableObject , you must call the CreateInstance() function from ScriptableObject class.要在从ScriptableObject继承时调用OnEnable()函数,您必须从ScriptableObject类调用CreateInstance()函数。

[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.使用ScriptableObject.CreateInstance的另一种方法是从另一个脚本中调用它。

[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>();
    }
}

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.根据我的经验,只有当活动场景组件引用 SO 的实例时,才会调用脚本化对象 (SO) 上的所有“事件方法”(OnEnable、Awake...)。 Just creating an instance is not enough.仅仅创建一个实例是不够的。 This is contrary to claims that "SOs live outside the scene".这与“SO 生活在现场之外”的说法相反。

It is particularly confusing in the editor.在编辑器中尤其令人困惑。 One can eg 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.例如,可以在项目的资产中创建 SO 的一个实例,它看起来像 OnEnable、Awake、... 正在按照您的期望被调用。 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.但是,一旦您在编辑器之外构建和运行游戏,如果 SO 实例没有分配给活动场景对象或由脚本以其他方式加载,则这些方法将不再被调用,而只是存在于资产。

Furthermore I couldn't find out when the editor decides to load or re-load an SO instance that only exists in the assets.此外,我无法确定编辑器何时决定加载或重新加载仅存在于资产中的 SO 实例。 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.我读到有更多的错误导致这些方法在 2018 版 Unity 之前与 SO 一起发出,现在它更可靠,但关于 SO 的文档今天仍然不准确和误导。

As @Vinz mentioned, an SO must meet one of the following to be activated:正如@Vinz 提到的,SO 必须满足以下条件之一才能被激活:

  1. Be referenced by a MonoBehaviour, or another SO which is.被 MonoBehaviour 或其他 SO 引用。
  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.关于第二个选项,似乎一旦检查它们被认为对该统一会话active ,但这是非常不一致的。

My solution is to have a MonoBehaviour whose sole job is to reference the Scriptable Objects.我的解决方案是拥有一个 MonoBehaviour,其唯一工作是引用 Scriptable Objects。 When this is in the scene and referencing your SOs, they will be consistently called in edit mode and for builds.当它在场景中并引用您的 SO 时,它们将始终在编辑模式和构建中调用。

using UnityEngine;


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

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM