How do I ensure no more than one instance of each of many GameObjects? (Singleton doesn't seem to scale well this way.)

The singleton pattern ensures a one instance of a class and this is useful pattern to apply when you have one or a few things to manage this way. Statement’s answer to the following question demonstrates this solution for a DontDestroyOnLoad object showing up upon the same scene being loaded more than once.

What’s a good way to get this kind of behavior for many game objects (hundreds per scene) in the hierarchy, when many of those could be from the same prefab? I don’t want to write a class for each one. I could look them up by name or tag to see if one already exists, but there are also too many objects to name them all conveniently. I could automate unique names, but I’d rather keep that field available for regular use.

Seems like I just need a way to uniquely identify runtime objects by their hierarchy object, or some identifier that is unique to a runtime object but is the same on each scene load. The docs don’t make any claims about the latter being guaranteed for object.instanceID.

Big thanks to all of the tips and tricks from @Fattie. I am already using several of them in my project and saving some for other needs.

Still, I wanted the ability to retain some state info on certain scene-specific objects for the entire game session, in a way that had all of the following properties:

  • Was scalable and had a nice dev pipeline when scaled to many hundreds of objects per scene
  • Didn’t require writing to disk
  • Didn’t require naming or tagging every such object or having a unique name or tag (manually or automated)
  • Didn’t duplicate objects if I revisited scenes
  • Didn’t require use of place-marker objects or repositioning objects. (The editor is really nice for WYSIWYG, I wanted to use it!)
  • Didn’t require doing complex layouts in duplicate scenes (the preScene idea seems elegant, but only when scene-specific layout for many objects isn’t needed).
  • Didn’t require a separate class for each object
  • Didn’t rely on the undocumented trait of InstanceID to increment per instantiation
  • Would work even if many of the objects used the same prefab
  • Probably some other criteria that I’m forgetting, but which disqualified other approaches

What I ended up doing:

  • Created a Persist component in C# that is applied to each object or prefab that I want to persist in the way I described.
  • Had the Persist component manage object state including setting the object inactive if it didn’t belong in the current level or shouldn’t otherwise be seen (like a permanently dead boss). It affects this state in the built-in callback OnLevelWasLoaded
  • Defined a struct that covered what I thought would identify an editor-placed object as unique (or identify erroneous object placement like a duplicate that I forgot to re-position). This struct had sceneIndex, object name (which would often be the prefab name where unique names are automatically enforced), and transform coordinates.
  • I used that struct (a value type) as a key to a static Dictionary for the game object.
  • In Awake() for the Persist class I had a try / catch statement add that object to the dictionary and Destroy it if it was already present. This (with the struct idea) approximated the Singleton pattern in the way I was looking for in my original question.
  • I had a global, static, DoNotDestroyOnLoad manager object reactivate objects in OnLevelWasLoaded if they’re meant for that scene and should still appear to the player. (Objects can deactivate themselves, but disabled objects’ scripts won’t run to reactivate themselves.)

Bonus learning:

  • Unity isn’t ready for native tuples
  • Unity doesn’t like a wide variety of hacks for multi-key dictionaries…just use a struct
  • If DoNotDestroyOnLoad objects are getting destroyed when you load a new scene, it’s probably because the parent is getting destroyed, e.g. an empty game object “folder” in the hierarchy.