[英]Unity3D MissingReferenceException when removing BoxCollider
我正在为Unity3D开发一个开源编辑器工具https://github.com/JAFS6/BoxStairsTool ,我正在编写一个CustomEditor。
我创建了一个主GameObject ,并将我的脚本BoxStairs附加到它上面。 这个脚本attachs相同的游戏对象一个BoxCollider。
在我的CustomEditor代码上,我有一个方法,负责删除之前附加的两个组件,以完成编辑。
这是代码:
private void FinalizeStairs ()
{
Undo.SetCurrentGroupName("Finalize stairs");
BoxStairs script = (BoxStairs)target;
GameObject go = script.gameObject;
BoxCollider bc = go.GetComponent<BoxCollider>();
if (bc != null)
{
Undo.DestroyObjectImmediate(bc);
}
Undo.DestroyObjectImmediate(target);
}
按下按钮后,在方法OnInspectorGUI上调用此方法
public override void OnInspectorGUI ()
{
...
if (GUILayout.Button("Finalize stairs"))
{
FinalizeStairs();
}
}
这两种方法都在课堂上
[CustomEditor(typeof(BoxStairs))]
public sealed class BoxStairsEditor : Editor
它实际上删除了两个组件,但是一旦删除了BoxCollider,就会出现以下错误:
MissingReferenceException: The object of type 'BoxCollider' has been
destroyed but you are still trying to access it.
我试着通过查看跟踪找到错误发生的位置:
Your script should either check if it is null or you should not destroy the object.
UnityEditor.Editor.IsEnabled () (at C:/buildslave/unity/build/Editor/Mono/Inspector/Editor.cs:590)
UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor editor, Int32 editorIndex, Boolean rebuildOptimizedGUIBlock, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1154)
UnityEditor.InspectorWindow.DrawEditors (UnityEditor.Editor[] editors) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1030)
UnityEditor.InspectorWindow.OnGUI () (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:352)
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)
但我的脚本都没有出现在它上面。
我一直在寻找我引用BoxCollider的代码,唯一的地方就是它创建的地方,当创建楼梯时,一旦检查员发生了变化就会触发。
它在课堂上:
[ExecuteInEditMode]
[SelectionBase]
public sealed class BoxStairs : MonoBehaviour
这是代码:
/*
* This method creates a disabled BoxCollider which marks the volume defined by
* StairsWidth, StairsHeight, StairsDepth.
*/
private void AddSelectionBox ()
{
BoxCollider VolumeBox = Root.GetComponent<BoxCollider>();
if (VolumeBox == null)
{
VolumeBox = Root.AddComponent<BoxCollider>();
}
if (Pivot == PivotType.Downstairs)
{
VolumeBox.center = new Vector3(0, StairsHeight * 0.5f, StairsDepth * 0.5f);
}
else
{
VolumeBox.center = new Vector3(0, -StairsHeight * 0.5f, -StairsDepth * 0.5f);
}
VolumeBox.size = new Vector3(StairsWidth, StairsHeight, StairsDepth);
VolumeBox.enabled = false;
}
我试图评论这个方法的主体,以允许删除没有这个“参考”的BoxCollider ,但错误仍然出现,所以,我想这个方法不是问题。
另外,我手动删除了BoxCollider ,没有单击Finalize按钮来触发此代码,通过右键单击检查器“删除组件”选项上的组件并且错误没有出现,之后,单击结束楼梯并没有问题出现。
正如@JoeBlow在评论中提到的,我已经检查过FinalizeStairs方法只被调用一次 。
此外,我已经通过调用AddSelectionBox方法检查了创建过程,它在点击结束按钮时没有发生。
所以,请我亲自动手。 这是开发分支的链接https://github.com/JAFS6/BoxStairsTool/tree/feature/BoxStairsTool ,在这里你会发现上面提到的方法FinalizeStairs只有代码才能删除BoxStairs脚本,就在那一刻它没有错误。
对此有任何想法或建议将非常有帮助。 提前致谢。
编辑:最小,完整和可验证的示例:
资产/ BoxStairs.cs
using UnityEngine;
using System.Collections.Generic;
namespace BoxStairsTool
{
[ExecuteInEditMode]
[SelectionBase]
public sealed class BoxStairs : MonoBehaviour
{
private GameObject Root;
private void Start ()
{
Root = this.gameObject;
this.AddSelectionBox();
}
private void AddSelectionBox()
{
BoxCollider VolumeBox = Root.GetComponent<BoxCollider>();
if (VolumeBox == null)
{
VolumeBox = Root.AddComponent<BoxCollider>();
}
VolumeBox.size = new Vector3(20, 20, 20);
VolumeBox.enabled = false;
}
}
}
资产\\编辑\\ BoxStairsEditor.cs
using UnityEngine;
using UnityEditor;
namespace BoxStairsTool
{
[CustomEditor(typeof(BoxStairs))]
public sealed class BoxStairsEditor : Editor
{
private const string DefaultName = "BoxStairs";
[MenuItem("GameObject/3D Object/BoxStairs")]
private static void CreateBoxStairsGO ()
{
GameObject BoxStairs = new GameObject(DefaultName);
BoxStairs.AddComponent<BoxStairs>();
if (Selection.transforms.Length == 1)
{
BoxStairs.transform.SetParent(Selection.transforms[0]);
BoxStairs.transform.localPosition = new Vector3(0,0,0);
}
Selection.activeGameObject = BoxStairs;
Undo.RegisterCreatedObjectUndo(BoxStairs, "Create BoxStairs");
}
public override void OnInspectorGUI ()
{
if (GUILayout.Button("Finalize stairs"))
{
FinalizeStairs();
}
}
private void FinalizeStairs ()
{
Undo.SetCurrentGroupName("Finalize stairs");
BoxStairs script = (BoxStairs)target;
GameObject go = script.gameObject;
BoxCollider bc = go.GetComponent<BoxCollider>();
if (bc != null)
{
Undo.DestroyObjectImmediate(bc);
}
Undo.DestroyObjectImmediate(target);
}
}
}
我是程序员,所以我只是调试找到问题(在我看来:D)。
MissingReferenceException:类型'BoxCollider'的对象已被销毁,但您仍在尝试访问它。
您的脚本应该检查它是否为null或者您不应该销毁该对象。
UnityEditor.Editor.IsEnabled()(在C:/buildslave/unity/build/Editor/Mono/Inspector/Editor.cs:590)
当代码在销毁Unity3D.Object后尝试访问它时,会发生MissingReferenceException。
让我们看一下UnityEditor.Editor.IsEnabled()
的反编译代码。
internal virtual bool IsEnabled()
{
UnityEngine.Object[] targets = this.targets;
for (int i = 0; i < targets.Length; i++)
{
UnityEngine.Object @object = targets[i];
if ((@object.hideFlags & HideFlags.NotEditable) != HideFlags.None)
{
return false;
}
if (EditorUtility.IsPersistent(@object) && !AssetDatabase.IsOpenForEdit(@object))
{
return false;
}
}
return true;
}
我们将无法知道哪一行是特定行590.但是,我们可以告诉MissingReferenceException
可以发生在哪里:
// ↓↓↓↓↓↓
if ((@object.hideFlags & HideFlags.NotEditable) != HideFlags.None)
@object
从分配Editor.targets其是被检查的所有对象的数组 。 在你的情况下,这个数组中应该只有一个目标对象 - BoxCollider
组件。
总之 ,在BoxCollider
组件上调用Undo.DestroyObjectImmediate
之后,检查器无法访问目标对象(我的意思是targets[0]
)。
如果你深入到检查(的反编译的代码UnityEditor.InspectorWindow
),你会看到被覆盖的OnInspectorGUI
功能是每个编辑器调用顺序 UnityEditor.InspectorWindow.DrawEditors
,包括BoxCollider的内部编辑器和您的自定义编辑BoxStairsEditor
的BoxStairs
。
OnInspectorGUI
Inspector显示的组件。 BoxCollider
的编辑器/检查器GUI。 BoxCollider
移动到BoxStairs
组件的上方。BoxCollider
。UnityEditorInternal.ComponentUtility.MoveComponentUp
时,此解决方案UnityEditorInternal.ComponentUtility.MoveComponentUp
。 但是,如果用户手动向上移动BoxCollider
组件,它可以在没有任何代码更改的情况下工作。 使用解决方案1后,NRE在Win10上的Unity3D 5.4上消失了。
using UnityEngine;
using UnityEditor;
namespace BoxStairsTool
{
[CustomEditor(typeof(BoxStairs))]
public sealed class BoxStairsEditor : Editor
{
private const string DefaultName = "BoxStairs";
[MenuItem("GameObject/3D Object/BoxStairs")]
private static void CreateBoxStairsGO ()
{
GameObject BoxStairs = new GameObject(DefaultName);
BoxStairs.AddComponent<BoxStairs>();
if (Selection.transforms.Length == 1)
{
BoxStairs.transform.SetParent(Selection.transforms[0]);
BoxStairs.transform.localPosition = new Vector3(0,0,0);
}
Selection.activeGameObject = BoxStairs;
Undo.RegisterCreatedObjectUndo(BoxStairs, "Create BoxStairs");
}
private void OnEnable ()
{
EditorApplication.update -= Update;
EditorApplication.update += Update;
}
public override void OnInspectorGUI ()
{
if (GUILayout.Button("Finalize stairs"))
{
needFinalize = true;
}
}
private void FinalizeStairs ()
{
Undo.SetCurrentGroupName("Finalize stairs");
BoxStairs script = (BoxStairs)target;
GameObject go = script.gameObject;
BoxCollider bc = go.GetComponent<BoxCollider>();
if (bc != null)
{
Undo.DestroyObjectImmediate(bc);
}
Undo.DestroyObjectImmediate(target);
}
bool needFinalize;
void Update()
{
if(needFinalize)
{
FinalizeStairs();
needFinalize = false;
EditorApplication.update -= Update;
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.