Don't "Find" GameObjects! Assign them

Never use “Find” to get a reference to a GameObject, neither by name nor by tag!

End of story. :flushed: :scream: :exploding_head:

Find by string considered painful

Indexing anything by string is always problematic. It reuqires a simple typo or a harmelss rename and you get to bughunt, posslbly for hours. The previous sentence contains three typos. Find THOSE! :stuck_out_tongue:

There is NOTHING in the scene that indicates to you or anyone else: DO NOT RENAME THIS OBJECT OR ELSE THE CODE BREAKS (also, there is mold in the fridge AGAIN, didn’t you all read the notes?).

You WILL forget eventually and rename an object or tag that you “Find” it by string in your scripts. Sooner or later, you will also come to realize that instantiated prefab objects get “(Clone)” and duplicated objects get “(1)” appended to their names. How do you deal with that? Best if you don’t have to.

Find is getting slower and slowerr aaandd sslooowweeerrr …

GameObject.Find is also slow, and it gets gradually slower and slower as you keep adding objects to the scene. Ain’t that obvious? Finding X scales proportionally with the number of places where you have to look for a possible X. Or where … it even matters where in the hierarchy X is, so simply by moving objects around in the scene can alter the code’s performance - for the better or worse.

Also, what if there is more than one thing with the same name? Which one will you find? You like playing russian roulette, don’t you? :wink:

Inspector drag & drop reference assignment

The most straightforward solution to most cases is to drag & drop the reference in the Inspector. It only requires a serialized field to make a reference assignable in the Inspector:

[SerializeField] private GameObject theGameObjectYouAreLookingFor;

This is even LESS code. And less code is always faster.

Of course a public field would also work but making fields public in any class/struct that isn’t a simple data transfer object (DTO) is against encapsulation and best practice.

Assigning Components works the same way

Oh and, most often you don’t actually need the GameObject. Perhaps only the Transform? Or a custom component? Works the same way:

[SerializeField] private Transform target;
[SerializeField] private PlayerController theComponentThatMovesThePlayer;

Now just drag and drop any GameObject that already has that component on it, and it will assign the object’s theComponentThatMovesThePlayer component automatically. This spares you the additional GetComponent<PlayerController>() calls.

Scene references in prefabs?

You can only assign scene references onto scene objects. A prefab cannot reference an in-scene object. If you have a prefab instance in your scene and apply the changes back to the prefab, the reference will not be persisted. Annoyingly, it may continue to work for a while but then the reference will be “missing”, at the latest after closing and re-opening the project.

For prefabs to get ahold of references to objects and components in the scene, the common solution would be a singleton.

Personally, I use the Components Registry approach which is essentially a singleton, except the singleton is wrapped so that you merely need to call var player = Components.Player; to get that reference.

You just need to make sure the reference is assigned before it is read. The rule of thumb is:


Note: UI Toolkit references elements by string. This is unfortunately the way it is for the time being. But at least it’s limited to UI documents, not the entire scene hierarchy where you are more likely to unexpectedly rename an element only to find your code is now broken.

3 Likes

I love this, only gripe I have is this:

If I was to design code that Instantiates and destroys objects as the game runs, or has a fluid system that many aspects can use but are adapted to each situation, then “Find” is best as it simply does it without need to allocate everything, I can have it where (Using UI as example) I have one set of variables for my UI, dictate who is to use that UI, whether it is my health, boss health, NPC health, all in different places, and use the “Find” Object to auto fill in each instance at runtime, adapting it on the fly.

Slightly slower, but on a grand scale, not noticeable

You still don‘t need „Find“ for this. :wink:

Anything that needs to be „found“ is (should be) acquired a reference of only by a script in one of the parent objects. Calling GetComponent(s)InChildren does the same job as „Find“ albeit faster, returns components in reliable order (depth-first search), without forfeiting type safety and most importantly, it won‘t find the stray random object anywhere in the hierarchy that just so happens to have a matching name (or even a partial match).

Also worth noting: I‘m practically never interested in a GameObject reference, unless it‘s a prefab reference for instantiation. In all other cases I need and work with a component, be it Transform, Rigidbody, Collider or custom MonoBehaviour. The GameObject merely provides a secondary „utility“ API, nothing but. Likewise, „Find“ is a second class citizen compared to GetComponent(s)(InParent)(InChildren).

And if you find yourself using „InParent“ think long and hard whether it shouldn‘t be the other way around. Prefer to look downward in the hierarchy, not up.

1 Like

The find is useful for when you have an object created at runtime, one that cannot be referenced easily in script and or editor. It allows me to create a form of random, I have yes parent class, but from that I can blend two or three into one as a (EnemyAI) for example, where either GameObject (of which transform, is child method of) so Referencing GameObject.transform is same as transform. alternate could well be this.gameObject.transform, which returns an auto direct reference to the object the script is attached to. I have scripts that auto add components, remove components when not needed and do quite a lot at runtime. Cutting out half the time needed to create gameObjects - Both in editor and at runtime, I override some of the Unity settings too, calculating formulae based on settings and real life equivalents.

Half of this, would take twice as long if not automated

You still don’t need to use any .Find or related methods for that.

Remember Object.Instantiate returns the instantiated object. This is where you can inject references, or make this instantiated object available to other systems to gleam any information off of.

3 Likes

Thanks for the blogs/posts, they are great :wink:

I was trying to wrap my head around your Components Registry when I realize that it looks a lot like the Service Locator Pattern… Or maybe I misunderstood something :thinking:

It’s kind of similar, correct.

I learned after the fact that the Components Registry is actually Dependency Lookup, the grandfather of Dependency Injection.

Given the simplicity that lookup provides I wouldn’t understand why injection became all the rave if it weren’t for TDD. This also made me realize why I creep out seeing DI used in Unity production code when little to no unit testing is done.

DI is pretty much just a waste of resources and a source of confusion without applied TDD, whereas DL is trivial and relatable.

1 Like

The Components Registry is a classic example of the Service Locator pattern.

Some people mistakenly think that a class is only a Service Locator if it stores services in a generic dictionary, but this is not true - that is a Dynamic Service Locator, in particular. It’s also very common for a Service Locator implementation to define separate members for accessing each service.

Some consider Dynamic Service Locators in particular a code smell, because they (typically) offer an API that allows one to request a service of any type, while in reality only being able to deliver an arbitrary subset of them. This means that it’s easy to create code that compiles just fine, yet doesn’t work at runtime. But there are also some benefits to the Dynamic Service Locator pattern, like the fact that it can be more easily combined together with other generic systems.

Dependency Injection is a very simple pattern, and - whether they know it or not - all programmers use it all the time. Using serialized fields is dependency injection. Adding parameters to methods is dependency injection. Every time that a class or a method receives object that they depend on from the outside (rather than creating or locating them internally), that is dependency injection.

Using dependency injection makes a lot of sense in many different situations, not just when doing full-blown test-driven development. It’s just a very easy and convenient way to improve the flexibility and reusability of your methods and classes in general.

Because serialized fields in Unity don’t support cross-scene references by default, it’s common to use the Singleton pattern or Service Locator pattern instead to resolve references to components across scene and prefab instance boundaries. Dependency Injection can’t be used in this particular situation in Unity, without the help of a Dependency Injection framework.

Even if you don’t use a DI framework to resolve cross-scene references, DI itself is still a really useful pattern to keep in mind in general. You can mix and match dependency injection, service locators and singletons all in the same project, based on what makes the most sense in any given scenario :slightly_smiling_face: