top of page

How To Use GameObject.Find

What's the best way to use GameObject.Find?


This is one of the easier answers I like to give on the forum: You don't. Ever.

One of the most reliable indicators of newbie code in Unity is the presence of GameObject.Find. It's nearly ubiquitous in newbie code but utterly nonexistent in runtime code written by more experienced programmers, for three reasons:

  1. It's really easy to figure out how it works and make something work, quickly.

  2. It works in a wide variety of situations.

  3. There is always, absolutely, without exception, a better option available to accomplish the same task.

It's attractive to newbies because learning one function call lets you get a lot of things done pretty quickly. However, relying on GameObject.Find will cause a lot of problems in the long run - performance problems, reliability problems, false positives and false negatives. The larger your game gets, the more significant GameObject.Find's problems will become.


Plus, all the cool kids will make fun of you for using it.


So what is it about GameObject.Find that's so bad?


Speed: When you call GameObject.Find, Unity crawls through every single GameObject in the scene, comparing its name to the string you're searching for. By computer science standards, this is agonizingly slow - imagine trying to find a book in a library that's not alphabetized or sorted by Dewey decimals, for example.


Reliability: There are far too many things that can go wrong when using GO.F without being noticed right away. Forgot to capitalize one letter in either the object name or the function call? Error. In older versions of Unity, the engine would actually add (Clone) to the end of an instantiated GameObject's name - that would've broken it. This isn't a concern anymore, but any code (e.g. on the Asset Store) that was written for older versions of Unity that expected that? Error. But the most common issue for reliability is duplicate names. If you ever have a second object that has the same name as the one you're looking for, there is absolutely no way to know which of those objects it's going to find. And when it finds the wrong one? Error.


Worse, all of those errors are runtime errors, not compile time errors. That means that they don't show up when you write the code, only appearing when you start playtesting the game. In some cases, they only appear when you playtest extensively, finding that unusual edge case where a second object named "Score" can appear at the same time when you hit two blocks in quick succession.

No Code Complete: MonoDevelop, or your script editor of choice, can't possibly know what your GameObjects' names are when you're writing code, so it can't autocomplete them for you the way it can class and function names.

"But it's cool if I only use it in Start()," I hear you say. Right?

No.


Don't get me wrong. Limiting GO.F usage to Start() is definitely better than calling it every frame, or (yech) multiple times per frame, in the same sense that I'd much prefer to be stabbed in the eye once as opposed to getting stabbed in the eye 60 times a second for the next hour. It's faster, true. But really, the performance considerations of GO.F are not the only reason to avoid it, and limiting its usage to Start() does nothing to absolve its reliability issues.


What's the magic word I should use instead?

The only problem with replacing GameObject.Find is that there's not a single technique you replace it with. There are, in fact, probably dozens. The good news is that most of them aren't very complicated, and you only really need to learn five or six techniques - between them, these techniques should cover all the situations in which you may have previously been tempted to use GameObject.Find.

Each technique is described here in terms of speed, reliability, and versatility, but keep in mind that each one also covers a specific range of purposes.


GameObject.FindWithTag

GameObject myPlayer = GameObject.FindWithTag("Player");

GameObject.FindWithTag is a slight improvement on .Find. Rather than using the name of the GameObject, it uses the tag (which can be set just under the name field in the Inspector). It also has a sister function, FindGameObjectsWithTag, that will find all the objects with the given tag, good for groups of similar GameObjects.

Speed: FindWithTag is faster than .Find, because Unity creates a Dictionary of all GO's that are tagged - a Dictionary lookup is far faster than the "look through each object one by one" method employed by GameObject.Find, especially if you have a lot of objects. It's not the fastest technique listed, but only barely. You'll still need to call GetComponent after using this, which is not ideal, and many of the other methods get around that.

Reliability: FindWithTag is slightly more reliable than .Find, but this area is its biggest weakness compared to the other methods we'll discuss. It does reduce the likelihood of typos causing issues, but that's about it.

Versatility: The tag system in Unity is pretty limiting, largely in the fact that you can only apply one tag to a given GameObject. Maybe you can make your red team tagged "Red" and your blue team tagged "Blue", but then all the players on both teams are just done for tags. You can't then use the tag system to find your goalies, for example. The non-trivial chance that two schemas for tagging will collide ultimately seriously limits the usage of FindWithTag.

Overall, FindWithTag is probably the worst replacement for GO.F on the list. However, its speed and relative simplicity does make it worth keeping in your toolbox, even if it's used sparingly.


Public member assignment via Inspector

public class CreepyFollower : MonoBehaviour {
    public Transform target;

    void Update() {
        transform.LookAt(target.position);
        transform.Translate(Vector3.forward * Time.deltaTime);
    }
}

Ultimately, this is one of the Unity fundamentals that you're absolutely going to have to learn. A public variable can be assigned in the object's Inspector by drag and drop. It's best used for objects that have a logical relation to each other - a character might have a reference to his own weapon, for example.


Speed: Super-fast. Essentially instant, in fact. And you can use the exact type of component you need, too - if you find yourself calling target.GetComponent<Character>(), then you can make your target a Character instead, and avoid the GetComponent call altogether! Anything derived from UnityEngine.Object can be drag-dropped this way, most notably all Components (including Camera, Renderer, etc), and all MonoBehaviours (and by extension, all of your own scripts). One side note: someComponent.transform is faster (and slightly more reliable) than someTransform.GetComponent<SomeComponent>(), so opt for linking directly to the SomeComponent instead of the Transform (or the GameObject) when you have the option.

Reliability: Very reliable. When you drag and drop a reference into the object, you know it's going to be that one, specifically. No ifs, ands, or buts about it. The main catch to keep an eye on is that references from inside a prefab to outside of it, or between two different scenes, might get broken.

Versatility: As described above, this technique is best used on objects that not only have a logical relationship to each other, but a hierarchical relationship as well (although that's far from a solid rule).


Opportunistic Assignment

private GameObject thingThatHitMe;

void OnCollisionEnter(Collision c) {
    thingThatHitMe = c.gameObject;
}

void Update() {
    if (thingThatHitMe != null) {
        thingThatHitMe.transform.position = Vector3.zero;
    }
}

Ultimately, this technique is very similar to the previous technique, and has far too many ways to be made useful to hope to list them all here. Fundamentally, all it means is: when you have access to the thing you need, hold onto it and store it. The most common use cases involve collisions and raycasting, I think, but it can apply almost anywhere.

If used in combination with other techniques on this list, it can become very powerful indeed. For example, if ScriptMaster uses GetComponentsInChildren<ScriptSlave> to loop through its children, it can set ScriptSlave's "master" property to itself, and now every slave knows who its master is.


The main difference between this and assignment via inspector in terms of code is that your code will have to be written in such a way that this object being null doesn't cause errors, which usually means simply putting null checks as seen above - make sure it fails gracefully until the object has a valid reference.


Storage Upon Instantiation

public class ArraySpawner : MonoBehaviour {
    public GameObject arrayPrefab;
    public int count = 5;
    public Vector3 offset = new Vector3(1,0,0);

    private GameObject[] spawnedObjects;

    void Start() {
        spawnedObjects = new GameObject[count];
        for (int c=0;c<count;c++) {
            spawnedObjects[c] = Instantiate<GameObject>(arrayPrefab);
            spawnedObjects[c].transform.position = offset * c;
        }
    }

It seems to have escaped the notice of many a rookie Unity programmer that Instantiate returns the object that it has spawned, meaning it can be assigned immediately (and reliably) upon creation. I've seen many a piece of code where Instantiate is immediately followed by "something = GameObject.Find" for the very object that was just created. Such a waste!

I find this comes up a lot with intermediate programmers who are trying to code their first grid system as well, say for a puzzle game. Store the grid cells you've created in an array (it can be multidimensional if need be), and you'll always have quick, reliable access to them!

Speed: Instant, with the caveat that you'll need to use GetComponent to access particular components.

Reliability: As long as the reference to the original object is good, this will be, too.

Versatility: Obviously only useful when you're instantiating things.

GetComponentsInChildren

Renderer[] childRenderers;

void Start() {
    childRenderers = GetComponentsInChildren<Renderer>();
}

void Update() {
    foreach (Renderer r in childRenderers) {
        r.enabled = Input.GetKey(KeyCode.Space);
    }
}

This function is extremely useful for objects that are in a hierarchy. It'll catch all of a specific class within the hierarchy of a particular object. If you do need to filter these for whatever reason, you can still apply your own filtering logic when you go through them.

Speed: Not superfast, but option the fastest you can do for the sort of situations it's used for. Take advantage of caching!

Reliability: Very solid.

Versatility: Extremely useful for stuff within a hierarchy.

Singleton

public class MainInterface : MonoBehaviour {
    public static MainInterface instance;
    public Text someTextObject;

    void Awake() {
        instance = this;
    }
}

//in any other script file, anywhere
MainInterface.instance.someTextObject.text = "Hello world!";

(The above is an extremely barebones implementation of a singleton; see the dedicated article on singletons for more complete versions.)

Singletons are one of those programming tools that are so useful and simple, you actually have to be careful not to use them too much - which is the only reason this technique is so far down this list. A singleton is a static reference to an object that is the sole instance of a given class in a scene. In other words, it's a single object that's accessible from anywhere. If this sounds extremely useful and the answer to all of your problems, that's exactly what it is! ...until you realize three months from now that you need a second instance of the Player class because you're adding network support, and now you're in trouble because you've written singletons all over the place, and it no longer makes sense.

So I'll say this once: A Singleton is best used when there can only logically be one of that class in existence. Generally, the best time to use a singleton is for the user interface. A few other good examples of singletons are manager classes, network connectivity management, and so on. You can use them to access the player character too, but be cautious when doing so - it's really easy to get trapped and make it impossible to add network play or AI opponents without significant rewrites.

Speed: Instant.

Reliability: If implemented as it is above, good but not great; there are a number of edge cases in which you can get snagged on a null reference. However, if implemented correctly, a singleton can be absolutely rock-solid. See the singleton article for more information.

Versatility: As stated, you should only use this if there will only ever be one of something. But if there are more than one, and you want something similar? Read on!

Multiton (maintained static list)

public class Character : MonoBehaviour {
    public static List<Character> all = new List<Character>();
    void OnEnable() {
        all.Add(this);
    }
    void OnDisable() {
        all.Remove(this);
    }
}

//in any other script file, anywhere
foreach (Character c in Character.all) {
    Debug.Log("Character named "+c.gameObject.name+" is at "+c.transform.position);
}

(The above is an extremely barebones implementation of a multiton; see the dedicated article on singletons/multitons for more complete versions.)

So you like the idea of a Singleton, but you have more than one thing? No problem! The multiton is a list that objects add and remove themselves to and from as they are created and destroyed. Just like a singleton, the multiton's list is available anywhere, anytime.

Speed: Not quite as fast as a singleton, but very close - only slower because you may need to loop through them. Still pretty much instant, though.

Reliability: Just like the singleton, it depends on the implementation. Above is the simplest and least reliable example, but the article about them will have examples of a much better implementation.

Versatility: Definitely more versatile than a singleton, especially because it doesn't rule out other techniques in the process. You can have a list of all characters readily available via Character.all, but still opportunistically assign them during collisions, for example.

FindObject(s)OfType

void Start() {
    Camera[] allCamerasInScene = FindObjectsOfType<Camera>();
}

FindObjectsOfType is a useful method to find all of a given class in the scene. There's not much more to it than that.

Speed: It's worth keeping in mind that this method is slow - potentially slower than GameObject.Find, even. If you find yourself calling this more than once or twice in a scene, you're probably better off with a Multiton as described above, if you're able. Thus, this method is most commonly used on classes to which you're not able to add your own OnEnable/Disable methods to, such as the builtin Unity classes.

Reliability: The main advantage that this has over GO.F is that you know exactly what you're getting back from it.

transform.Find

void Start() {
    Transform cameraLocation = transform.Find("Misc/Camera Placeholder");
}

If you absolutely, positively have to find something by name, then use transform.Find. But first, be really, really sure you absolutely have to find it by name. Are you sure you can't attach a component to the child, and find it with GetComponentInChildren? Are you sure you can't make a public reference, and assign it to that? If so, use one of those other methods.

In my experience, just about the only time this is truly necessary is when you need to find an object in the hierarch of an imported 3D object - for example, the location of where the camera should be for that cinematic scene you set up in Blender.

transform.Find is essentially what GameObject.Find would be if it were limited to one hierarchy instead of the whole scene. Because it's limited to one hierarchy, it's more reliable (less concern about false positives), more versatile (you can still use it if you have more than one copy of the thing in your scene), and faster (fewer objects to check). You can even include the entire hierarchal path to the transform you want in order to be sure that you get that specific one.

Unity's documentation on transform.Find contains good information and code examples.

Related Sidenote: SendMessage and BroadcastMessage

I don't see SendMessage or BroadcastMessage much these days, but they're worth mentioning here in any case. SM and BM have much of the same appeal and the same drawbacks as GameObject.Find - they're easy to understand, but slow and imprecise and error-prone. (If any other script adds a function named TakeDamage that doesn't mean exactly the same thing as the TakeDamage function you've already written, you run the risk of calling the wrong one.) And, indeed, many of the above solutions would be very handy for replacing SendMessage and BroadcastMessage, in particular GetComponentsInChildren.

There's one additional tool in the chest for replacing these, and that is interfaces. An interface is, quite simply, a description that you apply to a class saying, "This class implements these particular functions, and they are available for your use." By using an interface, the compiler knows that you want the Enemy class and the Player class to both have a TakeDamage function that can be called when a bullet hits their collider, but that TakeDamage function has no chance of being confused with another function that is coincidentally named TakeDamage down the line.

Interfaces are "inherited" kind of like classes, so to distinguish them from classes, they conventionally start with an "I". Interfaces can be used in GetComponent calls, for convenience. (According to the documentation, interfaces can not be used in FindObjectsOfType calls.)

interface IDamageTaker {
    void TakeDamage(float damageValue);
}

public class Enemy : MonoBehaviour, IDamageTaker {
    public float life  = 100f;
    public void TakeDamage(float damageValue) {
        life -= damageValue;
        if (life < 0f) Debug.Log("ded");
    }
}

//in your bullet script
void OnCollisionEnter(Collision c) {
    IDamageTaker[] allDamage = c.gameObject.GetComponentsInChildren<IDamageTaker>();
    foreach (var dmg in allDamage) {
        dmg.TakeDamage(50f);
    }
}

תגובות


bottom of page