0

我正在从模板创建一个新脚本。但是,在程序集有机会重新编译之前,无法访问这种新的脚本类型。我正在尝试创建新 ScriptableObject 的实例,以便通过代码生成它。

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

一切正常,直到我使用 CreateInstance(); 此时,该类型还不存在。我必须等待大会重新编译并投降类型......

我在谷歌上搜索了刷新大会的概念,但没有找到任何东西。

我还使用 async/await 谷歌搜索,以延迟调用 CreateInstance(),直到在程序集之后,肯定有新类型......到目前为止,这些尝试要么锁定了编辑器,要么没有按预期工作...(我是 C# 中这种异步风格的新手)

我对解决此问题的组装解决方案或异步解决方案持开放态度。

4

2 回答 2

0

一种解决方案是在 EditorPrefs 中保存创建脚本对象的请求,并在脚本重新加载后在回调中创建资产。不幸的是,如果不锁定编辑器,您将无法做到这一点。

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
    }
}
于 2019-06-27T21:16:04.187 回答
0

这就是最终的工作:

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

当用户按下 EditorWindow 中的“添加”按钮时,布尔值设置为 true:

  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' 委托需要订阅代码以添加新对象。它还需要进行布尔保护,以确保它仅在用户发出新的 Add 时运行。

于 2019-07-01T16:28:47.813 回答