Не вызывается функция OnEnable из ScriptableObject

#c# #unity3d #unity5

#c# #unity-игровой движок

Вопрос:

В следующем скрипте, как я могу получить путь к скрипту в папке 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 );
    }
}
  

Проблема в том, что OnEnabled она не вызывается, также кажется, что единственный способ получить путь к скрипту — через AssetDatabase.GetAssetPath , для которого требуется экземпляр.

Версия в Unity равна 5.5.0b3.

Ответ №1:

Чтобы OnEnable() функция вызывалась при наследовании от ScriptableObject , вы должны вызвать CreateInstance() функцию из ScriptableObject класса.

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

Другой способ использования 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);
    }
}
  

Тест:

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

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

Ответ №2:

По моему опыту, все «методы событий» (OnEnable, Awake, …) для скриптовых объектов (SO) вызываются только тогда, когда на экземпляр SO ссылается активный компонент сцены. Простого создания экземпляра недостаточно. Это противоречит утверждениям о том, что «SOs живут вне сцены».

Это особенно сбивает с толку в редакторе. Можно, например, создать экземпляр SO в ресурсах проекта, и это будет выглядеть так, как будто OnEnable, Awake, … вызывается, как вы и ожидали. Но как только вы создаете и запускаете игру вне редактора, эти методы вообще больше не будут вызываться, если экземпляр SO не был назначен активному объекту сцены или загружен скриптом другим способом, а просто существует в ресурсах.

Кроме того, я не смог выяснить, когда редактор решает загрузить или повторно загрузить экземпляр SO, который существует только в ресурсах. По моему опыту, это было очень непоследовательно и ненадежно.

Я читал, что было больше ошибок, из-за которых эти методы запускались с помощью SOs до версий Unity 2018 и что сейчас это более надежно, но документация по SOs по-прежнему неточна и вводит в заблуждение сегодня.

Ответ №3:

Как упоминал @Vinz, для активации SO должен соответствовать одному из следующих параметров:

  1. На нее ссылается MonoBehaviour или другой SO, который есть.
  2. Быть проверенным в редакторе

Что касается второго варианта, кажется, что при проверке один раз они рассматриваются active для этого сеанса unity, но это очень непоследовательно.

Мое решение состоит в том, чтобы иметь MonoBehaviour, единственной задачей которого является ссылаться на скриптовые объекты. Когда это происходит в сцене и ссылается на ваш SOs, они будут последовательно вызываться в режиме редактирования и для сборок.

 using UnityEngine;


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

введите описание изображения здесь