- Home /
How to destroy linked components when object is destroyed?
I have a component which manages other components on the same object in the editor. I've implemented OnDestroy so that the primary component removes all of the ones it manages. This works fine when I want to delete the main component. However, if I delete the game object, I get the error:
Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.
I can't seem to find a way around this. I'm testing for null before destroying and setting the references to null afterward, but I think Unity is trying to delete each component again after I've already deleted it. Is there a better way to handle the destruction of managed components?
My work around for now is to use a separate hidden child object to store the components, providing more certainty in destruction order. But I don't like this because it alters the hierarchy. I'm trying to keep the managed components hidden and unobtrusive. I've tried working with ScriptableObject as an alternative, but ran into insurmountable issues with prefabs and object duplication.
I should have mentioned too: If I don't delete the managed components, then they remain after deleting the primary. There are potentially a lot of components and they're hidden, so it's important that they get deleted with their owner.
Can you save the gameObjects to an array, pop them in a coroutine, and delete them in the next frame? Not an elegant solution, but I've never seen what you describe, so I'm not sure how to help you.
Also, could you consider using ScriptableObject rather than game objects if all you're doing is storing components?
Since this is all being done in editor mode (not playing), I can't make use of yield or coroutines. And yes, I have tried the ScriptableObject route but it poses difficult problems with duplicating and prefabs.
Sorry, I somehow completely missed that this was happening in the editor. I'll have to give it some more thought.
Answer by beck · Feb 24, 2014 at 03:21 AM
I had this same issue and I was able to solve it by passing the destroy code in an anonymous function to EditorApplication.delayCall.
[ExecuteInEditMode]
public class MyComponent : MonoBehaviour
{
[Serializable]
List<UnityEngine.Object> dependants = new List<UnityEngine.Object>();
void OnDestroy ()
{
#if UNITY_EDITOR
foreach(var d in dependants)
{
var o = d; // save in a different buffer to save state for anon func
EditorApplication.delayCall += ()=>
{
if(o) GameObject.DestroyImmediate(o);
};
}
#else
foreach(var d in dependants)
{
if(d) GameObject.Destroy(d);
}
#endif
}
Well done Beck! That snippet just saved me some frustration...
This prevents the error when deleting the game object or opening a new scene however when I press play the child components I created in editor mode are destroyed and are not available in play mode. Any suggestions on how you got around this?
Solved: Actually I got around this by making the child component a ScriptableObject. It was originally just a Serializable class. The problem was that serializable classes are garbage collected on scene load because Unity can't detect that they are referenced in the scene. When I made the component a ScriptableObject it was not garbage collected because Unity detected that it had a reference in the controlling class which is in the scene.
Update: I now have a problem that I can't save the ScriptableObject in a prefab so I'm going to got back to C# serialisation and look for another solution.
@fawnha You may be able to get around this by testing if(EditorApplication.isPlayingOrWillChangePlay$$anonymous$$ode)
The hell.. this one random thread saved me hours of google search deadzones... delayCall via onGUIChanged worked a treat.
Give this man a gold star. I would like to point out that #if UNITY_EDITOR applies, you still need to check for if(Application.isPlaying) and use Destroy if it is according to best practices.