Never use “Find” to get a reference to a GameObject, neither by name nor by tag!
End of story.
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!
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?
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:
Awake
is where you assign references but never “call” any of their methodsStart
orOnEnable
or any other event that executes afterAwake
is for accessing these references.
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.