Stop Looking, You Already Found It!

Wherever Unity is discussed I see a mistake repeated by new Unity developers. It’s not a dumb or obvious mistake, but it can be a costly one. It’s the use of Unity’s ‘find’ functions to locate gameobjects and components inside update methods. Here’s the kind of thing I’m talking about: [code language=”csharp”] void Update() { var player = GameObject.Find("Player").GetComponent<PlayerScript>(); // do something with ‘player’ } [/code] That line of code is perfectly fine Unity code, but using it inside the Update() method is a big mistake…

Finding is slow

boxImagine your scene components (the player, the enemies, the scripts, etc.) are all in a big, disorganised box. When you use a finding method, like GameObject.FindGameObjectWithTag, Unity trawls through that box to find what you’re asking for. This is quite slow (for reasons I won’t go into). When a particular piece of code requires (relatively) lots of computing power we call it ‘expensive’, and it should be used carefully.

Repeating slow things is very slow

The Update() method in Unity games runs once per frame, so all code in Update() typically runs 30 to 60 times per second! The same applies to LateUpdate() and FixedUpdate(). Keep expensive code out of the update methods so it’s not repeated unnecessarily. If you need to make the player move in every frame, don’t do this: [code language=”csharp”] void Update() { var playerMove = GameObject.Find(“Player”).GetComponent<MoveScript>(); playerMove.Move(); } [/code] That code will work correctly, but it’s inefficient. What if you could just set the Player object aside somewhere Unity could grab it when needed instead of trawling through that big box? Well you can. Simply create a variable in your script, then assign it to point to the object when your scene starts, in the Start() method: [code language=”csharp”] MoveScript playerMove; void Start() { playerMove = GameObject.Find(“Player”).GetComponent<MoveScript>(); } void Update() { playerMove.Move(); } [/code] In the Start() method the expensive search is done (once). From that point on, a reference to the relevant script is easily accessible in the playerScript variable. You’re no longer searching for it during every frame. When I say GameObject.Find and its relatives are expensive, this is relatively speaking. You should not avoid using them entirely, and they won’t affect performance in most cases.

Caching References

You should also apply this to getting components of the current gameobject. Instead of calling this every time you need to access the rigidbody2d: [code language=”csharp”] var rbody = gameObject.GetComponent<RigidBody2D>(); [/code] Cache that variable in Start() and then use rbody to access the rigidbody2d.

Looping

You should also avoid costly searching in loops. Consider the following: [code language=”csharp”] for (int i=0; i < 200; I++) { var player = GameObject.Find("Player"); Player.DoSomething(); } [/code] and: [code language=”csharp”] var player = GameObject.Find("Player"); for (int i=0; i < 200; I++) { player.DoSomething(); } [/code] In the first example Unity searched through the big box of components 200 times, but in the second example the slow search is done once. Now imagine if you put that first loop in Update()! You’d be doing a costly component search 200 times every frame. That would be 6,000 calls (at 30 fps) every second (if your device can keep up).

Cleaner Code

A side benefit of caching references is that it helps keep your code clear. You assign a value to player once, and it can be accessed throughout your script. Your methods and functions don’t need to find objects for themselves, and your code is easier to read and maintain.  

Summary

Finding game components and objects using Unity’s built-in functions (‘finds’ and ‘gets’ like GameObject.Find and GetComponent) is slow. Using those functions is not in itself troublesome; in fact, they are invaluable tools. But you should never repeatedly search for the same items when you can search once and ‘remember’ the object by caching a reference (usually at the beginning of the scene in Start()), and then use the reference whenever you need to access the object.

Leave a Reply

Your email address will not be published.