OnValidate and destroying objects

I’m using OnValidate (first time I’ve used that function, incidentally), and I’ve run into a significant and nonsensical problem with it: there’s no way to destroy objects. Destroying (and recreating) child obejcts is like 99% of what I would ever want to use OnValidate for.

Let me elaborate. So I tried to use DestroyImmediate, because this is an editor function and that’s what you do in the editor. It gives me this:

Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.
UnityEngine.Object:smile:estroyImmediate(Object)
BranchGenerator:OnValidate() (at Assets/BranchGenerator.cs:19)
UnityEditor.DockArea:OnGUI()

Weird, I think. Why would it want me to use Destroy in OnValidate, which can only ever be run outside play mode? I give it a shot, and as expected:

Destroy may not be called from edit mode! Use DestroyImmediate instead.
Also think twice if you really want to destroy something in edit mode. Since this will destroy objects permanently.
UnityEngine.Object:smile:estroy(Object)
BranchGenerator:OnValidate() (at Assets/BranchGenerator.cs:19)

So, let me reiterate: Why would it want me to use Destroy in OnValidate, which can only ever be run outside play mode? Also, how the hell am I supposed to destroy objects in OnValidate?

(And on a related note: I’ve never understood the reason for having Destroy and DestroyImmediate be two different functions. Just have Destroy see if we’re in Editor mode, and if appropriate call DestroyImmediate.)

6 Likes

There is a more fundamental difference to those functions. DestroyImmediate() really does immediately destroy the data right away whereas Destroy() waits until script execution has finished and only then destroys the object(s). This is much safer as it won’t destroy objects that are actively being worked on.

Also, it’s actually the reverse concerning play mode, i.e. you can only call Destroy() while in play mode.

However, the situation you are describing with one referring you to the other and producing a stalemate where you can call neither is a rather unfortunate one. That warrants deeper investigation…

3 Likes

I understand they behave differently, but why two different functions? Why not simply note the different behavior in the docs - “When called from outside Play Mode, Destroy will destroy the object immediately; when called from inside play mode, it will wait until the end of script execution.” There is never a situation where you can actually choose which one to use, right? It’s always one or the other.

In the current state of things, I have to do this ridiculousness anytime I have a generator function that may be called from either play mode or the editor:

 if (Application.isPlaying) Destroy(thing);
else DestroyImmediate(thing);
2 Likes

You can’t destroy object from OnInvalidate(), but you can:

void OnValidate()
{
     UnityEditor.EditorApplication.delayCall+=()=>
     {
          DestroyImmediate(objectToDestroy);
     };
}
44 Likes

I know this is 6 months old so, in case this is still of any interest to you…

In edit mode, DestroyImmediate remains unsafe. I’m encountering cases where the editor crashes on me, or hangs, because it’s not happy about when I invoke this function. Yes, these are often the cases when it turns out to be most useful.

You can postpone destruction in a variety of ways as PrefabEvolution already suggested.

Even so, DestroyImmediate has the tricky advantage that it does what it says, and does it right away. Having unwanted objects lingering inside the runtime often ends up with code doing things with an object that shouldn’t be there.
Although its use is strongly discouraged, calling DestroyImmediate() at runtime isn’t technically forbidden (unless that changed recently?); you do it at your own risk - a risk no greater than deleting Objects in C++.

Since I’m strongly against GC style object management (because I don’t want objects to exceed their welcome in my runtime) I would rather these two functions continue to exist side by side.

Destroy() is still very useful and reliable enough in 99% of cases. Would be nice to be able to use it in edit mode; I understand edit mode would never honour the destroy request in the current implementation. Does it mean it cannot be made to work?

1 Like

The issue is still there in Unity 4.6.3, but now attempting to use DestroyImmediate() results in slightly different message:
Destroying components immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.

Still displaying this confusing (and ultimately invalid) message in 5.3.4p3
It would be even better to include a link to this thread in the error message…

2 Likes

If you will not make scene dirty, changes will not be saved.

1 Like

This just saved me. <3

5 Likes

2019 and still saving asses.
You’re a genius! :smile:

3 Likes

Yea, this worked perfectly for me. Huge thanks for this crazy piece of code.

2 Likes

Thank you! For some reason I was able to run:

IEnumerator Destroy(List list)
{
yield return new WaitForEndOfFrame();
foreach (GameObject go in list) {
DestroyImmediate(go);
}

until today when I changed some references from Scene objects to Scriptable objects. Baffles me why this worked before, haha.

This ridiculousness is still alive and well in 2020.2.7, 8 years later

3 Likes

Friendly advice here!
Instead of DestroyInmediate, use the Undo version for it: otherwise you can’t undo the destruction!
This code will take care of the destruction in editor + make it undoable :slight_smile:

void OnValidate()
{
     UnityEditor.EditorApplication.delayCall+=()=>
     {
          UnityEditor.Undo.DestroyObjectImmediate(objectToDestroy);
     };
}

Oh and by the way, you can use it for multiple objects (I use it to destroy several components that require each other) and it will still record a single UNDO step, which is nice :slight_smile:

void OnValidate()
{
     UnityEditor.EditorApplication.delayCall+=()=>
     {
          UnityEditor.Undo.DestroyObjectImmediate(objectToDestroy_1);
          UnityEditor.Undo.DestroyObjectImmediate(objectToDestroy_2);
          UnityEditor.Undo.DestroyObjectImmediate(objectToDestroy_3);
          UnityEditor.Undo.DestroyObjectImmediate(objectToDestroy_4);
     };
}
3 Likes

Ahahaha, this is till a thing. Unity’s great 5-years of doing nothing for basic functionality hits once more.

1 Like

To be honest, this issue is the least of my concerns about Unity.

2 Likes

For all those in future, oddly enough, it works if you go via a coroutine:

void OnValdidate(){
            foreach (Transform child in transform) {
                StartCoroutine(DestroyGO(child.gameObject));
            }
}
    IEnumerator DestroyGO(GameObject go) {
        yield return new WaitForSeconds(0);
        DestroyImmediate(go);
    }
5 Likes

Thanks @maewionn ! Using Unity 2020.3 this Coroutine solution was the only approach that I was able to get working when running in the editor.

1 Like

Also here in 2021, wondering why DestroyImmediate is blocked from being used in OnValidate. Thank you for the Coroutine solution

I know this is an old thread but another approach would be to implement your own Destroy() Method in the MonoBehaviour you want to destroy and call it with Invoke()