来自 UnityEditor.InspectorWindow.GetEditorsWithPreviews 的 NullReferenceException

NullReferenceException from UnityEditor.InspectorWindow.GetEditorsWithPreviews

我有一个非常复杂的用于修改模型的 Unity 编辑器脚本。通过在资产文件夹中创建一个新对象,然后在其检查器中为其提供模型、一些修改器并按下应用来使用它。这会修改模型并将创建的对象替换为新的对象预制件。

当按下应用时,方法Apply()被调用,之后有运行,控制台打印出这个:

NullReferenceException: Object reference not set to an instance of an object UnityEditor.InspectorWindow.GetEditorsWithPreviews (UnityEditor.Editor[] editors) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:515) UnityEditor.InspectorWindow.DrawPreviewAndLabels () (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:612) UnityEditor.InspectorWindow.OnGUI () (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:398) System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222) Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation. System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232) System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115) UnityEditor.HostView.Invoke (System.String methodName, System.Object obj) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:272) UnityEditor.HostView.Invoke (System.String methodName) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:265) UnityEditor.HostView.InvokeOnGUI (Rect onGUIPosition) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:232)

我发现 2014 年的一个问题有类似的错误,但情况不同,没有答案 here

我检查了 MattRix's decompiled Unity repo 可能导致异常的原因,即使存储库的版本为 2017.1.0f3 而我使用的是 2017.1.1f1,我想我找到了出现问题的地方。 在 DrawPreviewsAndLabels():

IPreviewable[] editorsWithPreviews = this.GetEditorsWithPreviews(this.tracker.activeEditors);

然后

GetEditorsWithPreviews(Editor[] editors){
...
for (int i = 0; i < editors.Length; i++)
            {
                Editor editor = editors[i];
...

似乎在 Editor editor = editors[i]; 行调用了异常。这让我认为 this.tracker.activeEditors 是一个包含 null 元素的列表。这就是我坚持的地方。

什么可能导致此错误,我可以做什么或检查什么来修复此错误?

编辑:有人指出这可能意味着 this.tracker.activeEditors 本身为空。如果是这样的话,我认为异常会发生在 editors.Length 并且或者至少它不应该进入 for 循环。由于行号可能随着 Unity 版本的变化而改变,该代码中的行实际上可能不是异常的原因,但我无法知道。

我无法轻松修复此空指针的问题是它发生在编辑器代码中,而我的代码没有任何调用,我不知道该怎么做才能 this.tracker.activeEditors 或任何导致它的原因正确分配到应有的位置。

我是否必须让一些可能不活跃的东西活跃起来,以便有活跃的编辑,不管那是什么意思?

这是我的Apply()方法:

private void Apply(List<Mesh> meshes, List<Material[]> materials, Chamferer chamferer)
{
    GameObject newObject = Instantiate(chamferer.gameObject);
    Object targetPrefab = PrefabUtility.GetPrefabObject(chamferer);

    string name = chamferer.source.name;
    //Delete previous meshes
    MeshFilter[] filters = newObject.transform.GetComponentsInChildren<MeshFilter>();
    for (int i = 0; i < filters.Length; i++)
    {
        filters[i].transform.SetParent(null);
        GameObject.DestroyImmediate(filters[i].gameObject);
    }

    string prefabPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(targetPrefab));

    Mesh prev = null;
    //Add new meshes
    for (int i = 0; i < meshes.Count; i++)
    {
        Mesh mesh = meshes[i];
        Mesh newMesh = EdgeChamfer.AddChamfer(mesh, chamferer.scale);
        GameObject obj = new GameObject(mesh.name);
        obj.transform.SetParent(newObject.transform);
        MeshFilter mf = obj.AddComponent<MeshFilter>();
        MeshRenderer mr = obj.AddComponent<MeshRenderer>();
        mf.sharedMesh = newMesh;
        mr.sharedMaterials = materials[i];

        //Adds mesh under prefab
        if (i == 0)
        {
            AssetDatabase.CreateAsset(newMesh, prefabPath + "/" + name + ".asset");
            prev = newMesh;
        }
        else
        {
            AssetDatabase.AddObjectToAsset(newMesh, prev);
            prev = newMesh;
        }
    }
    AssetDatabase.SaveAssets();

    GameObject newPrefab = PrefabUtility.ReplacePrefab(newObject, targetPrefab);

    //Renames the object accordingly
    string renamingerr = AssetDatabase.RenameAsset(prefabPath + "/" + newPrefab.name + ".prefab", "Chamfered" + name);
    if (renamingerr != "")
    {
        Debug.Log(renamingerr);
    }
    Selection.activeObject = newPrefab;
    EditorGUIUtility.PingObject(newPrefab);
    GameObject.DestroyImmediate(newObject);
}

完整 GetEditorsWithPreviews() 来自 MattRix's repo:

public IPreviewable[] GetEditorsWithPreviews(Editor[] editors)
        {
            IList<IPreviewable> list = new List<IPreviewable>();
            int num = -1;
            for (int i = 0; i < editors.Length; i++)
            {
                Editor editor = editors[i];
                num++;
                if (!(editor.target == null))
                {
                    if (!EditorUtility.IsPersistent(editor.target) || !(AssetDatabase.GetAssetPath(editor.target) != AssetDatabase.GetAssetPath(editors[0].target)))
                    {
                        if (EditorUtility.IsPersistent(editors[0].target) || !EditorUtility.IsPersistent(editor.target))
                        {
                            if (!this.ShouldCullEditor(editors, num))
                            {
                                if (!(editors[0] is AssetImporterEditor) || editor is AssetImporterEditor)
                                {
                                    if (editor.HasPreviewGUI())
                                    {
                                        list.Add(editor);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            foreach (IPreviewable current in this.m_Previews)
            {
                if (current.HasPreviewGUI())
                {
                    list.Add(current);
                }
            }
            return list.ToArray<IPreviewable>();
        }

似乎 OnInspectorGUI() 在替换对象后尝试执行某些操作时可能会出现错误。

如果不替换预制件而是创建新的预制件,则不会出现该错误。 所以在代码中,通过替换

解决了问题
GameObject newPrefab = PrefabUtility.ReplacePrefab(newObject, targetPrefab);

在“应用()”中与

    GameObject newPrefab = PrefabUtility.CreatePrefab(prefabPath + "/" + name + ".prefab", newObject);