- Home /
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.
http://answers.unity3d.com/questions/326470/dontdestroyonload-issue.html
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.
(1) What is a "scene hierarchy objects". Do you mean GameObjects?
http://docs.unity3d.com/Documentation/ScriptReference/GameObject.html
(2) it would be incredibly simpler if you explained in five words what you are doing. ie is it an $$anonymous$$$$anonymous$$P, is it a first person game, is it multiplayer, is it a 2.5D game, etc.
(3) in your final paragraph, what is a "hierarchy object"? Do you mean the parent? You can easily get the parent of something, or find objects under a given parent.
(4) Just TBC .. are you aware of just "tags" in Unity, which may utterly answer all your concerns in all ways.
PS never use singletons in Unity. just forget about them. singletons can't be monobehaviours - ie you can't attach them to objects in the normal was as a component; they can't operate "over time" (can't use coroutines) and so on. For some calculation or data that you access everywhere, just use a static object if you must.
The answer is easy as pie (if I understand your question).
In practice, in every single Unity game (or game in any game engine of similar paradigm):
You have a scene called "preEverything" (or whatever catchy name you come up with).
Anything that has to exist throughout the whole game, put it in that scene, and indeed make it DontDestroyOnLoad.
the only purpose of the setup scene, is to load all "DontDestroyOnLoad" material, and then you'll have one line of code, which, loads whatever your "actually first" scene is.[see footnote]
(So TBC, as a developer, whenever you "hit play" to Play your project in the editor, in fact, you have to click first to the preEverything scene.)
It's just one of those "weird things" Unity doesn't mention anywhere:
In practice, the "DontDestroyOnLoad" concept is utterly useless, unless specifically you are using it in such a "pre-everything" setup scene.
Simply, the concept "DontDestroyOnLoad" is only sensible / workable, if in fact you have such a scene ("preEverything") the purpose of which is to create all such DontDestroyOnLoad objects.
You will find that once you change to this model, EVERYTHING is very easy and sensible.
[footnote] Just to be clear. 100% of real-world projects have "special scenes" that do all sorts of setup for the game, before beginning (say) your opening graphics, etc. For example,you'll have to set up perhaps the player inventory or whatever, connect to the internet, do crap for the marketing department, and so on and on. 100% of real world projects have often many scenes like this. Note that what I'm describing, the preEverything scene, is EARLIER than these. It is literally the scene which LAUNCHES these "preli$$anonymous$$ary scenes", the preEverything scene does launch whatever your "first" scene is (including the absolutely typical "preli$$anonymous$$ary" scenes.
ALL THAT BEING SAID,
.
it's easy as pie to ensure that a game object which is DontDestroyOnLoad remains the only one.
Add a line of code in Awake ..
if there is another one of me in the scene (same gameobject.name),
look at both of our object.instanceID
if I am the larger integer, DestroyImmediate(gameObject);
it's that simple! heh.
a better way to do it is something like this
class variable ... bool IAmTheFirst;
Awake
if there is another one of me in the scene (same gameobject.name),
if (! IAmTheFirst), DestroyImmediate(gameObject);
IAmTheFirst = true
obviously, you have to remember to ensure that each thing has a unique name, but that is de rigueur (that's french for "de rigueur") anyways.
I've always felt it is wrong and flakey to take this approach though. It's just "not how it's meant to work". But there it is.
Ah! This seems like a great lead.
I get how this works and I do have many objects where this concept will work directly, thanks!
On the other hand, I also have lots of objects that need to exist only for one scene, but they need to retain state if the scene is revisited. For example, a pickup item needs to remain gone if picked up once, even if the player went back to the scene. It would be nice if these objects were self-managing. For example I could script a component to set the object inactive if it's the wrong scene or if it's already been picked up, but set the object active if it's the right scene and hasn't yet been picked up. I would need to use DontDestroyOnLoad for these to retain state and therefore I'd need to prevent duplicates for these as well.
Do you feel like the preScene concept is a good approach for these one-scene objects too? What if I have hundreds of these objects per scene? How would I handle positioning each object without having a complete copy of the environment in the preScene?
Thanks for sharing some cool tricks!
"On the other hand, I also have lots of objects that need to exist only for one scene, but they need to retain state if the scene is revisited. For example, a pickup item needs to remain gone if picked up once, even if the player went back to the scene. It would be nice if these objects were self-managing"
Simple!
Note that these objects very much are persistent throughout the game. It's just that you're saying sometimes the player can't see them.
So, you have to program that in. Often a ludicrously simple solution is they sit offscreen!!!! (or perhaps "under the ground" or whatever is relevant in your game - we still are totally clueless what paradigm your game is)
you'd be astounded how much stuff sits offscreen even in big titles eh!
I'm not sure of your exact solution, but it is absolutely definitely the case that the things you describe are "existing" (in your "story" if you will) one time and persistently forever. it's just that sometimes they are "invisible" "not available to the player" or whatever is relevant to you.
Yep, I meant "exist for one scene" from the player's perspective. Bad choice of words! I usually like SetActive(false) to hide these lingering things when the player's not supposed to know they're there so that the scripts aren't running either and I don't have to track positional data for putting them back.
I'm trying to figure out a good pipeline for placing these things in a preScene. I guess I'd lay them out in the real scene to get positioning right, then cut and paste them to the otherwise empty preScene. Seems pretty straightforward to do manually, but might be tricky to automate.
Yes. I guess I was trying to indicate GameObjects that exist because they're placed in the editor, as opposed to prefabs instantiated at runtime. I suppose it's irrelevant since they're both GameObjects? Let me try to edit the language.
Sure, happy to try to help but just do't know what you want.
It is 100% universal to gave a "preLoad" scene, the raison d'etre of which is to have all DontDestroyOnLoad objects in it.
Also -- do tags solve your needs?
regarding 2) I'm trying to get some light scene-to-scene persistence in a game that has no write to disk, as it looks like you surmised in your answer below.
3) By "hierarchy object" I meant the GameObject in the editor's hierarchy view that will get instantiated each time the scene is loaded. I was trying to see a way to tell if the instantiation had occurred more than once.
4) yep, I know of tags. Just given the volume of things I'm working with, it would be nice not to deal in tags, even if I could automate the tagging.
Gleaned what looks like a very helpful answer below, thanks! Will dig in deeper.
Answer by s_guy · Oct 01, 2013 at 05:43 AM
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.
I'm only using the preScene motif for a few DontDestroyOnLoad things with dynamic position that have a starting point into a scene enforced by script (and therefore don't need positioning against an environment). I'm not persisting the ground between unity scenes because the ground doesn't need to show up to the player in any scene other than its own and I don't need it for layout guidance in a preScene because this solution lets me do WYSIWYG layout in the scene where the object is supposed to manifest.
By "native tuples" I mean the tuple class in System.Tuple from c# 4.0 isn't yet supported by Unity as far as I can tell.
http://msdn.microsoft.com/en-us/library/system.tuple.aspx
I tried to use an N-tuple as a data structure for identifying my editor-placed objects. The native one isn't supported and Unity also didn't like me trying to make my own struct that approximated the real implementation. It gave weird compile errors in unrelated, previously working code when I implemented the data structure described in the following (which works for me in a non-Unity c# project).
gotchya on the ground. (It's very hard to visualise what sort of project you're working on!)
thanks for the Tuple info, had utterly not heard of it.
BTW if you did not notice already, look on your build settings for iOS. there's that thing where you almost always in practice want the smallest .NET subset. (see heaps of posts on here about it) so bear in $$anonymous$$d. (indeed similarly if you can, strip down to the micro mscorlib, I guess.)