- Home /
The question is answered.
Destroying Components from Editor Script Throwing MissingReferenceException
I'm trying to use a custom editor script to remove multiple BoxCollider2D components on a game object.
I have a script attached to the game object in the scene to which I am adding the components to. I have an editor button that calls this method inside the game object's script.
public void GenerateWallCollider2D()
{
for (int i = 0; i < 4; i++)
{
gameObject.AddComponent("BoxCollider2D");
}
}
These components appear correctly on the game object. I then have another editor button that calls a method to remove all the BoxCollider2D components that I have added.
public void Clear()
{
BoxCollider2D[] boxCollidersRemove = GetComponents<BoxCollider2D>();
for (int i = 0; i < boxCollidersRemove.Length; i++)
{
DestroyImmediate(boxCollidersRemove[i]);
}
}
This correctly removes all the BoxCollider2D components from the game object but there is a problem. Unity throws an error:
MissingReferenceException: The object of type 'BoxCollider2D' 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.
This error only goes away if I remove the DestroyImmediate() line but of course this removes that functionality which is what I'm looking for.
After moving around with where I was keeping my BoxCollider2D[] reference it doesn't seem to matter. Checking if the object in the array is null doesn't solve the issue. TryCatch blocks don't catch the exception ( it acts as though it didn't throw anything ).
From what I could tell it seems like Unity refreshes the game object when a single component is removed, so when it goes to the next index in the array it looks like it's acting as if the object at that index is referenced to nothing. Which would make sense except it still deleted the correct component. This is a problem because if I were to use this during runtime it constantly pauses the game due to this error.
So I'm at a loss as to what the problem is. I feel like I may be misunderstanding the way Unity manages adding and removing components or I'm missing some call in one of the scripts to cement the idea that I am remove a component? Is there a better way to remove multiple components that I'm not aware of? A lot of the questions about this similar problem on here mostly talk about Destroy() which does not work in editor. The others I've read have no answers.
Thanks!
Have you tried iterating backwards?
for (int i = boxCollidersRemove.Length - 1; i > -1; i--)
{
DestroyImmediate(boxCollidersRemove[i]);
}
You should first check unity official scripting guide about
public static function Destroy(obj: Object, t: float = 0.0F): void;
Why DestroyImmediate ins$$anonymous$$d of Destroy?
Without reading this entire thread, I would hazard a guess as to that being the problem. I'll go read the QA after I click the button :P
Normally, Destroy takes a frame or so to complete. DestroyImmediate does literally that and so anything which still references the object will run into this problem.
Also you may need to use Destroy(GetComponent(BoxCollider)) I could be wrong.
DestroyImmediate is used because this is an editor script. The regular Destroy method will never be called in editor executed scripts. This is the reason for the DestroyImmediate.
The reason this is an issue is that Destroy flags objects that it is called on for deletion at the end of the frame. DestroyImmediate deletes the object at that very instant ins$$anonymous$$d of flagging the object for removal.
As another answer to the original issue, if it is still an issue, after calling Destroy you need to repopulate your array of objects. You must do this as you have another script somewhere that has a reference to that object and is trying to do something with it.
Answer by StewVanB · Jun 14, 2014 at 06:45 AM
I wrote this today to see if I could recreate your error. For me this generate no errors. Place this script in your "Assets/Editor" folder. A new menu item will appear in the top bar that says "Test" from here there are two operations. AddCollider and DelCollider. Add will add a BoxCollider to every object in the scene. Delete will remove all colliders from every object in the scene.
using UnityEngine;
using System.Collections;
using UnityEditor;
public class ComponentEditor : MonoBehaviour {
public void doAddComponent(){
GameObject[] theList = GameObject.FindObjectsOfType<GameObject>();
foreach(GameObject obj in theList){
obj.AddComponent<BoxCollider>();
}
}
public void doDelComponent(){
Collider[] theList = (Collider[])Component.FindObjectsOfType<Collider>();
foreach(Collider col in theList){
DestroyImmediate(col);
}
}
}
--Basic editor GUI for "ComponentEditor.cs" --This should go in the "Editor" folder.
using UnityEngine;
using UnityEditor;
using System.Collections;
[CustomEditor(typeof(ComponentEditor))]
public class ComponentEditorVisuals : Editor {
public override void OnInspectorGUI(){
ComponentEditor myScript = (ComponentEditor)target;
EditorGUILayout.BeginHorizontal();
if(GUILayout.Button("Add Collider")){
myScript.doAddComponent();
}
if(GUILayout.Button("Del Colliders")){
myScript.doDelComponent();
}
EditorGUILayout.EndHorizontal();
}
}
Unfortunately Unity still gives me the same error with those changes.
Place a Debug.Log right after you make the BoxCollider array that prints boxColliderRemove[i].gameObject.name to see if the list is being populated correctly.
The array is being populated correctly. I put Debug.Log before and after the DestroyImmediate() and it showed that each component in the array was getting set to null after being destroyed. However when I cleared the colliders and put the inspector of the object into debug mode, it showed that the array contained elements that were considered "$$anonymous$$issing" by Unity. So to me it seems like the issue happens inside DestroyImmediate(). Setting each element to null after destroying it gives the correct result in the debug view in the inspector, however it does not solve the error since Unity throws an error before I can modify the element.
What is the end goal for this code? Since it is an editor script it can only be used in the editor and not in play mode. This makes me assume that you are just using it to make your life easier by mass adding/removing colliders?
The end goal is already met so yes, it is to easily create and clear colliders on an object. It works without issues already I just don't understand why I'm getting this error and would like to know. It just doesn't make sense to me.
Follow this Question
Related Questions
The name 'Joystick' does not denote a valid type ('not found') 2 Answers
"ArgumentException: method arguments are incompatible" for Analytics Event Tracker parameter 6 Answers
how to fix the "unityexception : launching ios project via xcode failed" exception ? 0 Answers