Managing GameObject connections across scenes

I know that using DontDestroyOnLoad will persist a GameObject across scenes, but it makes using that object within the next scene almost impossible. In particular, creating script variable connections, or adding the object to Unity Events in the Inspector.

Is there a way to create a reference to the shared object in the scene you are working on? So it doesn’t get added to the scene when you run it (because the shared version will be available instead). That way, you can still make connections with the object and it will use the shared version instead.

Am I missing something?

Depends on what the object is.

If this is a data container of some sort (ie. it doesn’t do anything, it’s just around so objects can share data), a ScriptableObject that is referenced in all the scenes can do the job.

If it’s a GameObject that’s instantiated at runtime, it should be accessed at runtime through some kind of static thing and not have to be assigned to objects in the editor.

If none of those cover your use case, can you be a bit more specific about what problem you’re facing?

I just mean a standard GameObject that has child objects that you may need to drag over to the inspector and connect with other scripts to make connections. For example, a global UI GameObject that you have connected with other objects in your scene to trigger alert boxes or whatever. If you are using Unity Events and want to drag your shared object into the inspector’s field to trigger functions. All this is really convenient and means I don’t have to write as much code. But, it all breaks if the object is shared across scenes and you can’t make the connections.

It all really does. I’m EXTREMELY allergic to dragging little bitsy-bobsy-poopsy kinda crud all over my UI: buttons, texts, sliders, etc. It’s a nightmare workflow once you reach a certain complexity, even just a few buttons. “Wait, how come that button isn’t working?!”

And then yeah, with a common game manager that lives across scenes, it all breaks.

I got tired of it and wrote my own UI interop package that I call Datasacks. I wrote it just to say goodbye to this ceaseless dragging and eventing, which is ALWAYS where the untestable unfindable bugs are located.

To use it, you subscribe to the UserIntent Datasack and unsubscribe when you’re gone.

In fact, I use it for tons of other application eventing, data storage, data sharing, etc.

Here’s how Datasacks works:

Datasacks is presently hosted at these locations:

https://bitbucket.org/kurtdekker/datasacks

If you are using any version of Unity later than Unity2021, it may be necessary to add this line to your Packages/manifest.json, or add it via the Package Mangler:

"com.unity.ugui": "1.0.0",
1 Like

ALSO… just to answer this question directly:

Do not EVER place a DontDestroyObject in a scene… just don’t do it.

Here’s how to make long(er)-lived stuff:

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.

There is never a reason to drag a GameObject into a scene if it will be DontDestroyOnLoad.

If you do:

  • you may drag it into something else that isn’t DDOL. FAIL
  • you may drag something else into it that isn’t DDOL. FAIL

Just DO NOT drag anything into a scene that will be marked DontDestroyOnLoad. Just Don’t Do It!

When working with multiple scenes, it boils down to serialization.

If Scene A has a reference to something in Scene B, then what should happen if you load Scene A when B isn’t loaded yet? All the tricks you have simply won’t work if the thing in scene B doesn’t exist.

It really is as simple as that. Unfortunately.

So there’s really no getting away from the fact that some coding will be required to deal with this. Either via singleton(s), events, or just plain old finding things at runtime and hooking it up. That’s basically what Camera.main does.

Or you could use a pre-existing solution like the one mentioned above that does all the hard work for you.

It’s possible to use a custom Guid-based solution to serialize cross-scene references.

Combined with some custom editor tooling, this can be used to enable dragging-and-dropping of references across scene boundaries, like I’ve done with Init(args):
cross-scene-references

Some other solutions I know of are SimpleXSR and Guid-References.

Here’s a Unite talk on the topic:

I have tried this or been on projects where this was tried sooooo many times.

It is ALWAYS without exception a nightmare when there is a bug.

We humans are really bad at staring at walls of numbers 482f4524-425599f45

That’s why I always use strings, and I almost always use meaningful strings.

Every single time you get a log saying “Not found: 482f4524-425599f45…” you now need to STOP work and start trying to figure out what this is.

Is it important? Is it trivial? Is it a weapon? Is it a button? Was it a legacy asset that was removed? Was there just a typo? Wait, I can’t find that string anywhere! Did it come from a savegame?

All of those questions need to be answered and now you need to write tooling to grep through all your stuff looking for GUIDS.

Meanwhile, my project will be saying “Not found: Button.Mainmenu.Play.CampaignPlus”

Hm… I think I know exactly where that is, and hey, I was just modifying that last week.

Be kind to yourself. You’re human. Use strings. You’ll thank yourself again and again and again.

1 Like

I agree, that would be a bad error message. I also save the name of the target game object and the scene that contains it, so that I can display useful human-readable information in the Inspector even when the scene in question isn’t loaded.

1 Like