6
\$\begingroup\$

Inside a MonoBehaviour MyComponentA I have a list of MyComponentB (also a MonoBehaviour) like so:

List<MyComponentB> myList;

This list gets populated mostly through editor scripts at edit time. Now say that someone who is editing the scene comes along and deletes a game object that has MyComponentB attached, and also happens to be referenced by the list above.

How can MyComponentA fix the list to remove null references after such a deletion? I basically want to make this automatic so that when designers delete these objects, they dont have to go hunt down which MyComponentA referenced them and fix the list.

My naive approach was to have a circular reference from MyComponentB to MyComponentA (so that it knows which list it belongs in), and then unregister it properly when OnDestroy gets called. This however doesn't work at edit time unfortunately since OnDestroy is not called.

Thanks for your help!

\$\endgroup\$
  • \$\begingroup\$ i cant understand your question very well. when an object is deleted in unity even if its in editor or play mode, all of its referencess set free from the ram automatically. \$\endgroup\$ – virtouso Oct 11 at 7:54
  • 1
    \$\begingroup\$ @virtouso What I get from the question is that the OP has a list that is referencing Components (or GameObjects) and when Unity destroys these, the lists will still have an entry but it will reference to null. I have had that question before though I currently don't remember how I fixed it :(. Iirc you will get errors saying that you are referencing a destroyed gameobject. \$\endgroup\$ – D.Kallan Oct 11 at 8:17
  • \$\begingroup\$ @D.Kallan generally null checking is not a good approach in programming as results in defencive programming but in this case you dont have another choice. on deletion event you can write a code that removes every object that is equal to null in the list \$\endgroup\$ – virtouso Oct 11 at 8:50
2
\$\begingroup\$

Although there are probably multiple ways to do this, the one you already mentioned is probably the simplest one to implement with the least amount of edge cases in which it doesn't work... That being said, if someone has a better approach, I'm interested aswell.

By adding [ExecuteInEditMode] you can get your original idea to work. Note that this means that all other Unity methods will be called outside of playmode aswell (like Start, Update, OnGUI, etc.).

First the class which contains the List:

public class Example2 : MonoBehaviour
{
    public List<GameObject> listOfGo = new List<GameObject>(); // This works well with Undo
    public List<Example> listOfExample = new List<Example>(); // This works, but not with Undo :( therefore I advise storing the GameObject instead as done in the list above
}

Then the other class that removes itself from the list:

[ExecuteInEditMode]
public class Example : MonoBehaviour
{
    [SerializeField] private Example2 _parent;

#if UNITY_EDITOR
    private void OnDestroy()
    {
        if (!Application.IsPlaying(this))
        {
            // Register Undo, so that when you ctrl-z the delete operation, it is re-added to the list
            UnityEditor.Undo.RecordObject(_parent, "Remove from parent");

            // Remove from list
            _parent.listOfGo.Remove(gameObject);
            _parent.listOfExample.Remove(this);
        }
    }
#endif
}

Note that i above example, the ListOfExample is only added to show that it doesn't work with Undo, you don't actually need to use 2 lists ;)

You might be able to do this using Undo.postprocessModifications instead but I haven't tried this, I also think that even if you'd get it to work using that callback, it is probably not very robust as Unity might change stuff behind the scenes that you would rely on with that approach...

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.