简体   繁体   中英

Reload Assembly for access to new ScriptableObject script type

I am creating a new script from a template. However, this new script type is not accessible until the Assembly has had a chance to re-compile. I am trying to create an instance of the new ScriptableObject, in order to generate it through code.

public override void Def()
{
  NewAssetName = EditorGUILayout.TextField(NewAssetName);

  EditorGUILayout.BeginHorizontal();
  if (GUILayout.Button("Add"))
  {                    
    TemplateLines = File.ReadAllLines("E:/Unity/Editor/Data/Resources/ScriptTemplates/NewScriptableObject.cs.txt");

    for (int i = 0; i < TemplateLines.Length; i++)
                        {
    if (TemplateLines[i].Contains("#SCRIPTNAME#"))
    {
      TemplateLines[i] = TemplateLines[i].Replace("#SCRIPTNAME#", NewAssetName);
    }
  }

  NewFilePath = "Assets/" + NewAssetName + '/' + NewAssetName + ".cs";
  NewSOPath = "Assets/" + NewAssetName + '/' + NewAssetName + ".asset";

  File.WriteAllLines(NewFilePath, TemplateLines);

  ScriptableObject NewSO = CreateInstance(TypeName);        
  AssetDatabase.CreateAsset(NewSO, NewSOPath);
}

Everything works fine, up until I use CreateInstance(); At this point, the type does not exist yet. I have to wait for the Assembly to re-compile and capitulate the type...

I have Googled the concept of refreshing the Assembly, but have not found anything.

I have also Googled using async/await in order to delay calling CreateInstance(), until AFTER the Assembly, for sure, has the new type... So far, these attempts have either locked up the Editor, or do not work as intended... (I am new to this async style in C#)

I am open to either an Assembly solution, or an async solution, for solving this problem.

One solution is to save a request to create your scriptable object in EditorPrefs and create your asset in a callback after scripts reloaded. Unfortunately, you can't do that without locking the editor.

using UnityEditor;
using UnityEngine;

public class EditorUtils
{
    [MenuItem("Tools/Create SO")]
    private static void CreateSO()
    {
        GenerateAndSaveSOClass();

        ShouldCreateSO = true;

        AssetDatabase.Refresh();
        AssetDatabase.SaveAssets();
    }

    private static bool ShouldCreateSO
    {
        get { return EditorPrefs.GetBool("should_create", false); }
        set { EditorPrefs.SetBool("should_create", value);}
    }

    [UnityEditor.Callbacks.DidReloadScripts]
    private static void OnScriptsReloaded()
    {
        if (ShouldCreateSO)
        {
            ShouldCreateSO = false;

            var so = ScriptableObject.CreateInstance("MyClassName");
            var path = "Assets/SO.asset";

            AssetDatabase.CreateAsset(so, path);
        }
    }

    private static void GenerateAndSaveSOClass()
    {
        // TODO: generate and save class
    }
}

This is what wound up working:

using UnityEditor;
using UnityEngine;

public class HandleNewScriptAndSO : ScriptableObject
{
    public StringRef ActiveDirectory_Ref;
    public StringRef NewSOName;
    public BoolRef TypeHasBeenAdded_Ref;

    private string NewSOPath;

    private void OnEnable()
    {
        AssemblyReloadEvents.afterAssemblyReload += GenerateNewSO;
    }

    public void GenerateNewSO()
    {
        if (TypeHasBeenAdded_Ref.Val)
        {            
            ScriptableObject NewSO = CreateInstance(NewSOName.Val);
            NewSOPath = ActiveDirectory_Ref.Val + '/' + NewSOName.Val + '/' + NewSOName.Val + ".asset";
            AssetDatabase.CreateAsset(NewSO, NewSOPath);

            TypeHasBeenAdded_Ref.Val = false;
        }            
    }        
}

The boolean is set to true when the user presses the 'Add' button in the EditorWindow:

  if (GUILayout.Button("Add") && NewSOName_Ref.Val != "")
  {
    AssetDatabase.CreateFolder(ActiveDirectory_Ref.Val, NewSOName_Ref.Val);

    TemplateLines = File.ReadAllLines("E:/Unity/Editor/Data/Resources/ScriptTemplates/NewScriptableObject.cs.txt");

    for (int i = 0; i < TemplateLines.Length; i++)
    {
      if (TemplateLines[i].Contains("#SCRIPTNAME#"))
      {
        TemplateLines[i] = TemplateLines[i].Replace("#SCRIPTNAME#", NewSOName_Ref.Val);
      }
    }

    NewFilePath = ActiveDirectory_Ref.Val + '/' + NewSOName_Ref.Val + '/' + NewSOName_Ref.Val + ".cs";

    File.WriteAllLines(NewFilePath, TemplateLines);

    TypeHasBeenAdded_Ref.Val = true;

    AssetDatabase.Refresh();

  }

'AssemblyReloadEvents.afterAssemblyReload' delegated needed to be subscribed with the code to add the new objects. It also needed to be bool-guarded, in order to make sure it only runs when the user issues a fresh Add.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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