When I was developing Unity games, I had learned that GameObject.Find
is slow once the size of object becomes bigger. Also, many game developers suggested against using such method, as it not only slows down the program, but also impacts the overall maintainability of the code. But I have never get a chance to quantify how slow will the game become if we exploit the usage of GameObject.Find
. Therefore, it is a good time to perform an experiment on these two methods. In this article, I will do a very simple experiment on the following methods. All of these methods perform the same action: Getting a reference to a game object.
GameObject.Find
- Set up the public variable in the script, then drag the other game object to the reference box in Unity Editor.
The development environment I am on is 2019.3.7.f1. I will perform the scripting in this experiment using C#. Here is how I set up the structure of the experiment:
- Independent variable: The method of accessing the reference to a
Cube
game object - Dependent variable: The time elapsed after scanning through all the
Cube
game object using the given method - The CPU, RAM usage, and other game objects (
Main Camera
,Directional Light
) are controlled and won't change.
Getting Started
Firstly, I created a new Unity project, as usual.
My brand new project looked as empty as this:
Testing the performance of direct referencing
To start testing, it's time to write some scripts -- under Assets
folder, I created scripts
directory and then added a new script, as you can see from the screen shot below:
Here is how the test is implemented. To test the performance of direct referencing an object in Editor, we firstly create 10,000 GameObjects using the following script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class FindTest : MonoBehaviour
{
private static int size = 10000;
public GameObject[] cubes = new GameObject[size];
void OnEnable()
{
cubes = new GameObject[size];
for (int i = 0; i < size; i++){
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = i.ToString();
cubes[i] = cube;
}
}
}
To use this script, simply create an empty game object. Then, on the right side, search for the Create
script. Once you load the Create
script, 10,000 cubes will pop up in the editor, and they are all binded to the cubes
array in Create
class.
Once we finished creating these GameObjects, the editor will look like this:
Since each cube has a BoxCollider
component, our goal is to turn off the BoxCollider
of all the components. We will measure how long does this action take.
We use the following script to perform the action, which is modified based upon the previous script: We removed the procedure for creating the cubes, and replaced them with cubes[i].GetComponent<BoxCollider>().enabled = false
.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Create : MonoBehaviour
{
private static int size = 10000;
public GameObject[] cubes;
void OnEnable()
{
for (int i = 0; i < size; i++){
cubes[i].GetComponent<BoxCollider>().enabled = false;
}
Debug.Log(Time.realtimeSinceStartup);
}
}
Once Unity finishes the action, the time elapsed (in seconds) will be sent to the console. We perform the same action 10 times. The console will then look like this, if we turn off clear on play
and all other options. The console will collect all the data once we finish the experiment.
We extracted the dataset into a spreadsheet.
The following table shows the result after this first experiment:
Now, let us move on to the second test, to see how does GameObject.Find()
performs.
Testing GameObject.Find()
Now let us test the performance of GameObject.Find()
. We can use the testing scripts we developed previously, with some slight modifications. In fact, we only modified one line -- changing from cubes[i].GetComponent<BoxCollider>().enabled = false;
to GameObject.Find(i.ToString()).GetComponent<BoxCollider>().enabled = false;
. This will change the way we access to the Cube objects.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Create : MonoBehaviour
{
private static int size = 10000;
public GameObject[] cubes;
void OnEnable()
{
for (int i = 0; i < size; i++){
GameObject.Find(i.ToString()).GetComponent<BoxCollider>().enabled = false; // Modified line
}
Debug.Log(Time.realtimeSinceStartup);
}
}
After performing the test 10 times (same as before), we recorded the following data -- indeed, as we can see, GameObject.Find()
took longer in accessing all the game objects and turn their BoxCollider
s off.
Testing on a smaller number of game objects
We have seen that GameObject.Find()
significantly lags behind when there is a large number of GameObjects in the scene. But what if we change the number of objects into a smaller one, e.g. 100? Let's do the same experiment with the smaller number.
We can see from the figure above there isn't a huge time difference between these two methods if we reduce the number of game objects in the experiment -- GameObject.Find
even has a tiny edge over direct referencing in this smaller experiment. After gathering all the data, we produced the following figure, whereas each bar represents the average time of using the corresponding method:
Conclusion
In conclusion, we can discover that GameObject.Find
has worse performance comparing to direct referencing in the experiment. This is potentially due to the fact that GameObject.Find
needs to linearly find the object with corresponding name, whereas the direct referencing method does not need to perform linear search. Overall, if one doesn't really need to use GameObject.Find
, it should be avoided.
However, in this experiment, we have only explored two of the many methods for referencing a game object in Unity. There are many methods that we can also include in this experiment. For instance, we have not tried FindObjectOfType
and FindGameObjectsWithTag
yet. These are the methods that we can include in the future experiments.
Top comments (1)
Really interesting! Keep up the awesome experiements!