Singleton - DontDestroyOnLoad only work for root GameObjects error

Well, firstly will say that I check a few similar threads but still cannot figure it out what I am doing wrong!
I am not pro dev, so yeah, probably I am not getting the idea very clearly but still trying.

The error:

DontDestroyOnLoad only work for root GameObjects or components on root GameObjects.
UnityEngine.Object:smile:ontDestroyOnLoad(Object)
Singleton`1:Awake() (at Assets/Scripts/Singleton.cs:31)

The error comes from:

DontDestroyOnLoad(gameObject);

Here is my code:

using UnityEngine;

public class Singleton<T> : MonoBehaviour  where T: MonoBehaviour
{
    private static T instance;

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = GameObject.FindObjectOfType<T>();

                if (instance == null)
                {
                    GameObject singleton = new GameObject(typeof(T).Name);
                    instance = singleton.AddComponent<T>();
                }
            }

            return instance;
        }
    }

    public virtual void Awake()
    {
        if (instance == null)
        {
            instance = this as T;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}

One more thing, Visual studio (mac version) shows me this hint:

Should I change it from GameObject just to Object?

1 Like

It means in the hierarchy, the game object with the code “DontDestroyOnLoad” must be the ‘root’ (ie: not a child game object).

14 Likes

Right, so basically…

Parent
-Child
–Grandchild

Your parent must be dontdestroyonload, not the other two.

As far as your Visual Studios warning. It just suggest you can simplify the code. You don’t have to do it if you write code and you understand it. All it’s really designed to do is removed parts of code that aren’t really needed to do what you try to do. If you right click on it, it should offer you an option to resolve it and what suggestions they have.

2 Likes

You may delete “GameObject”.
Use simple “instance = FindObjectOfType();”

still facing this issue? xD is the instance = FindObjectOfType(); worked?

That does not work for me. But i moved gameobject which the script attached to main root in hierarchy. I mean under scene and it worked.

I am having the same issue with the DontDestroyOnLoad. I can’t move all my objects to root due to dependencies within the UI… Anyway… when you say to put the script in the Parent… does it matter if the parent is only an empty object? Still not working when I add it to the Parent object. Thx…

Then you cannot singly mark it DontDestroyOnLoad() as long as it remains a child GameObject.

It’s really that simple.

The act of marking something DDOL (even just a script!) moves the GameObject (and ALL its children) physically to a temporary transient scene called DontDestroyOnLoad.

7369391--898277--ddol.png

This is why you can never ONLY mark a child GameObject as DDOL.

If you want, you can mark the entire hierarchy that a GameObject is within as DDOL, but this might have other issues.

You would accomplish this shotgun “DDOL ALL THE THINGS!” approach by:

// mark the root transform of my ENTIRE HIERARCHY as DDOL
DontDestroyOnLoad( transform.root.gameObject);

But use caution because the above comes with massive potential complications.

Execute the above code, press Pause and study what just happened in the Hierarchy; note what has been moved into the DontDestroyOnLoad meta scene.

1 Like

There are generally two solutions to the issue. However which one makes more sense depends on the usecase.

If you have a script on a child object that should call DontDestroyOnLoad you can either:

  • detach the child from the parent so it becomes a root object
  • or call DontDestroyOnLoad on the actual root object of the object tree

Both solutions may have different wanted / unwanted side effects.

So solution 1 is to do

transform.parent = null;
DontDestroyOnLoad(this);

solution 2 would be this:

DontDestroyOnLoad(transform.root);

The first solution will break the current hierarchy so it doesn’t work for example on UI elements which have to be under a Canvas object. However preserving such an object without the canvas wouldn’t make any sense in the first place.

The second solution will preserve the hierarchy as it is. It will simply mark the top most / root object of the current object tree as DontDestroyOnLoad which will ensure the script which calls this method remains loaded and the hierarchy is preserved. However it will of course preserve the whole object tree which may also contain other objects which you may not need.

If neither of those solutions fit your needs, you just need a different object structure and have to group things in a way so it makes sense.

Technically there’s a third solution which is essentially a hybrid of the two. If you just need a partial branch of the object tree you just need to detach that branch and call DDOL on the root of that branch once detached. In case of a canvas that is nested in some other gameobjects would mean to detach the canvas object and keep that alive which will keep the nested UI element alive that you’re interested in.

Hopefully people understand why only root objects are preserved. Imagine gameobjects as cartboard boxes to store objects in it. You can put smaller boxes into larger ones and store arbitrary items in those boxes. Those are your gameobjects. Now imagine you have a very important item, say the last picture of your grandma. So you put a shiny golded sticker on the back of the picture that reads “DO NOT TRASH THIS, IT’S IMPORTANT”. So anyone who cleans your room and reads that sign would not throw it in the trash. However if the picture is buried somewhere in a closed box, it will get thrown out with everything inside the boxes because who’s cleaning up the room doesn’t search through every nested box. Even if they did, in order to just keep the picture it has to be taken out of the box(es) so when the room as been cleared out, only that item will remain, directly inside that room. Of course instead of tagging the picture you could tag the outer box so when the cleaner goes through the things in the room it won’t touch the box that has the “keep” tag on.

Yes, real world analogies do not always make much sense and can not always be well transferred into the digital world, but I hope this gives a better understanding how it works, or more importantly why the other approaches do not work.

3 Likes

I have the same issue and the code below worked for me

  • DontDestroyOnLoad(transform.root);

I’m doing audio switch between scene to scene.

Instead of
DontDestroyOnLoad(gameObject);
I changed it to
DontDestroyOnLoad(transform.root.gameObject);
The error issue solved.\

Thanks.

5 Likes

The struggle disappears if you simply STOP placing DontDestroyOnLoad objects in scenes.

YES, ten billion tutorials tell you to do this. This is because tutorial writers generally only need their stuff to barely work. The moment you change a scene (or add a new level) you are in deep trouble with either duplicate DDOL objects or null references.

NEVER put a GameObject in any Scene if it is going to have DontDestroyOnLoad called on it.
Just Don’t Do It!! Seriously.

Simple Singleton (UnitySingleton):

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

These are pure-code solutions, DO NOT put anything into any scene, just access it via .Instance

Alternately you could start one up with a RuntimeInitializeOnLoad attribute.

The above solutions can be modified to additively load a scene instead, BUT scenes do not load until end of frame, which means your static factory cannot return the instance that will be in the to-be-loaded scene. This is a minor limitation that is simple to work around.

If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

public void DestroyThyself()
{
   Destroy(gameObject);
   Instance = null;    // because destroy doesn't happen until end of frame
}

There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

OR just make a custom ScriptableObject that has the shared fields you want for the duration of many scenes, and drag references to that one ScriptableObject instance into everything that needs it. It scales up to a certain point.

And finally there’s always just a simple “static locator” pattern you can use on MonoBehaviour-derived classes, just to give global access to them during their lifecycle.

WARNING: this does NOT control their uniqueness.

WARNING: this does NOT control their lifecycle.

public static MyClass Instance { get; private set; }

void OnEnable()
{
  Instance = this;
}
void OnDisable()
{
  Instance = null;     // keep everybody honest when we're not around
}

Anyone can get at it via MyClass.Instance., but only while it exists.

I’m almost entirely certain that the account you’re replying to is just a ChatGPT poster.

1 Like

OH YOU’RE RIGHT!!! This is the first “soft” tell:

The second tell is they put an invisible hyperlink at the end! Select all to see it.

9480412--1333168--Screen Shot 2023-11-18 at 1.26.02 PM.png

Shame on you dirty rotten @sr4218845 … I reported him but he’ll probably just spam all weekend long until they rip all his posts out.

1 Like

Thank you, had the same issue!