If I keep Invoke(nameof(DestroyThis), 5); line enabled, when InSceneScript gets destroyed, all children get reparented to under GlobalScript.
But if I turn off Invoke(nameof(DestroyThis), 5); and turn on Invoke(nameof(ChangeScene), 5); The children of InSceneScript get destroyed, regardless of the fact that they were reparented to under an object which is marked as DontDestroyOnLoad (GlobalScript).
This seems like a bug to me (and I reported this: (Case 1337272)). But I wanted to create this thread to make sure that I’m not missing anything.
When unloading the scene there is no guarantee that the GameObject containing InSceneScript gets unloaded before its children, so transform.childCount can return 0 when OnDestroy is called or the children might already be marked for destruction before they are reparented.
You can avoid this by not using SceneManager.LoadScene directly to change the scene but a custom method which broadcasts an event just before the loading takes place.
public static class SceneLoader
{
public static event Action<string> LoadingScene;
public static void LoadScene(string name)
{
if(LoadingScene != null)
{
LoadingScene(name);
}
SceneManager.LoadScene(name);
}
}
I put logs in OnDestroy method to print child count. Everytime child count was correct and all the children got successfully reparented (the child count of GlobalScript increased). But after OnDestroy finished, all the children that moved got destroyed. So it’s not a matter of order.
It seems like unity deatroys everything mindlessly when scene changes which is inconsistent with normal destruction and that’s why I think it’s a bug
Regarding your suggestion, unfortunately I don’t have access to code which loads the scene.
To give more context, This is a plugin code which uses an Object Pool that doesn’t get destroyed between scenes.
I need to return all the pooled items to the object pool when the script gets destroyed.
I don’t think that is what’s happening. It is more likely that the children have are already been marked to be destroyed, so even if you reparent them during OnDestroy they’ll still get removed by the end of the frame. It is similar to how Object.Destroy doesn’t fully destroy the target immediately but only after the end of current Update loop.
If you can’t modify the code that handles scene loading then I’d try if SceneManager.activeSceneChanged or SceneManager.sceneUnloaded actually get called early enough before OnDestroy. Hopefully they are just named misleadingly and can help you here
It is NOT similar to Object.Destroy. I tested it and this is the whole point of this post.
If you call Object.Destroy on a gameobject, you can reparent children in OnDestroy call of that object.
Children remain in Object.Destroy call. Children don’t remain in scene change.
Unfortunately moving children in both of those callbacks still produces the same problem.
The difference is that LoadScene affects all objects in the active scene, while in your test you only used Destroy on the single parent GameObject. It makes sense that the child GameObjects would remain unaffected in the latter scenario if they are moved away before the parent gets destroyed at the end of the Update loop.
If in your test you instead used Object.Destroy on all the child GameObjects as well and then tried to relocate them to a different scene I assume that wouldn’t work either.
I understand what Unity did when they unload a scene. My problem with what Unity is doing is that it is inconsistent what you can and cannot do in OnDestroy of a gameobject.
And to add insult to injury, there’s no alternative to what I want to do. I cannot even do that in OnDisable. I have no problems if there was a callback from Unity which signaled the scene is going to change. But it doesn’t have it. I have some pooled objects that I NEED to return to the pool. That’s the point of pooling. But Unity is providing no way to do that.
I think either destruction behavior needs to be consistent or an API needs to be added to allow moving of objects.