I have a pretty complicated Unity Editor Script for modifying models. It is used by creating a new object in the asset folder and then in its inspector giving it a model, some modifiers and pressing apply. That modifies the model and replaces the created object with the new object prefab.
When pressing apply, the method Apply()
is called and after that has run, the console prints out this:
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)
I found a question from 2014 with a similar error but different circumstances and no answer here.
I checked MattRix's decompiled Unity repo for what might cause the exception and even though the repo has version 2017.1.0f3 and I'm on 2017.1.1f1, I think i found the places where the problem occurs.
in DrawPreviewsAndLabels()
:
IPreviewable[] editorsWithPreviews = this.GetEditorsWithPreviews(this.tracker.activeEditors);
And then
GetEditorsWithPreviews(Editor[] editors){
...
for (int i = 0; i < editors.Length; i++)
{
Editor editor = editors[i];
...
The exception seems to be called on the line Editor editor = editors[i];
. This makes me think that this.tracker.activeEditors
is a list that has elements that are null
. This is where I'm stuck at.
What could cause this and what can I do or check to fix this error?
EDIT: It was brought out that it probably means that this.tracker.activeEditors
is itself null. If that were the case, I think the exception would happen at editors.Length
and or at least it shouldn't go into the for loop. Since the line numbers might have changed with Unity version change, The line in that code might not actually be the cause of the exception but I have no way of knowing that.
The problem why I cannot fix this nullpointer easily is that it happens in the editor code without any calls from my code and I do not know what to do so that this.tracker.activeEditors
or whatever is causing it would be assigned correctly where it should be.
Do I have to make something active that might not be so that there would be active editors, whatever that means?
This is my Apply()
method:
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);
}
And full GetEditorsWithPreviews()
from 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>();
}
It appears that the error might be appearing when OnInspectorGUI()
is trying to do something after the object is replaced.
The error does not appear if instead of replacing the prefab, a new prefab is created. So in code, the problem is solved by replacing
GameObject newPrefab = PrefabUtility.ReplacePrefab(newObject, targetPrefab);
in ´Apply()´ with
GameObject newPrefab = PrefabUtility.CreatePrefab(prefabPath + "/" + name + ".prefab", newObject);