Stop overusing GetComponents!

Recently, I see too many YouTube videos, tutorials, and questions in Unity groups where people are overusing GetComponent methods in their code. Time to talk about why you shouldn’t use it! Or at least use it sparingly.

Why am I bothering about that? Because I’m doing code reviews, and I just had enough of it. ?

YouTubers, tutors, Unity, and other content creators – YOU ARE RESPONSIBLE FOR MAKING PEOPLE DO BAD THINGS!

Now let’s try to fix it. ?

Why so dramatic? ?

As I said earlier, more and more people are using GetComponent methods in a bad way.

Unity’s documentation is a great resource, and I have to acknowledge it. But the problem is that people use examples from documentation in their code.

And the worst part is that this mistake can be easily avoided. Whenever someone explains what GetComponent is, he should add a disclaimer with BIG LETTERS when it’s fine to use it and when you shouldn’t!

So yeah, I have a problem with that, because later I have to deal with GetComponents in the code, which I really don’t like!

Code quality is the only thing that matter.

What’s the issue with GetComponent? ?

The GetComponent and all other methods like it in Unity are methods to find a component attached to GameObjects.

Each of these methods is going through a list of attached components. Each time it’s used.

This has real performance implications, and as a programmer, you should optimize your solutions!

Why I shouldn’t use it? ⛔️

As I already mentioned, whenever you call the GetComponent method, your code runs through a list of attached components.

I’m fine with this method when you are initializing your object or when you are running checks to detect what entered trigger.

But for God’s sake, NEVER do it in the Update or any other place which is called in the loop!

It’s killing me when I see it! There should be a special place in hell for people doing that in their code.

You don’t want to go there.

How can I do BETTER? ?‍?

There are many ways you can avoid using so many GetComponents in your code.

  1. Call it once to get component before using it.

DON’T

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Bad example, don't do it like that!
/// </summary>
public class DontDoThat : MonoBehaviour
{
    /// <summary>
    /// Set start values.
    /// </summary>
    void Start()
    {
        // Continuously getting component. Don't do it.
        gameObject.GetComponent<Text>().text = "Hello!";
        gameObject.GetComponent<Text>().color = Random.ColorHSV();
        gameObject.GetComponent<Text>().fontSize = Random.Range(14, 32);
    }
}

DO

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Good example, you can do it like that!
/// </summary>
public class DoThat : MonoBehaviour
{
    /// <summary>
    /// Set start values.
    /// </summary>
    void Start()
    {
        // Get component before you do something with it!
        var textComponent = gameObject.GetComponent<Text>();

        // Assign values.
        textComponent.text = "Hello!";
        textComponent.color = Random.ColorHSV();
        textComponent.fontSize = Random.Range(14, 32);
    }
}
  1. Save references in the variable.

DON’T

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Bad example, don't do it like that!
/// </summary>
public class DontDoThat : MonoBehaviour
{
    /// <summary>
    /// Set start values.
    /// </summary>
    void Start()
    {
        // Continuously getting component. Don't do it.
        gameObject.GetComponent<Text>().text = "Hello!";
        gameObject.GetComponent<Text>().color = Random.ColorHSV();
        gameObject.GetComponent<Text>().fontSize = Random.Range(14, 32);
    }

    /// <summary>
    /// Update values each frame.
    /// </summary>
    void Update()
    {
        // Continuously getting component in each frame! Big NO NO NO!
        gameObject.GetComponent<Text>().text = "Hello! Time: " + Time.realtimeSinceStartup;
        gameObject.GetComponent<Text>().color = Random.ColorHSV();
        gameObject.GetComponent<Text>().fontSize = Random.Range(14, 32);
    }
}

DO

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Good example, you can do it like that!
/// </summary>
public class DoThat : MonoBehaviour
{
    // Reference to the component which you can use in your code.
    private Text textComponent;

    /// <summary>
    /// Initialize component with references.
    /// </summary>
    void Awake()
    {
        // Get component before you do something with it!
        textComponent = gameObject.GetComponent<Text>();
    }

    /// <summary>
    /// Set start values.
    /// </summary>
    void Start()
    {
        // If you don't want to write Awake method, you can still get component reference here
        // textComponent = gameObject.GetComponent<Text>();

        // Set start values for the component.
        textComponent.text = "Hello!";
        textComponent.color = Random.ColorHSV();
        textComponent.fontSize = Random.Range(14, 32);
    }

    /// <summary>
    /// Update values each frame.
    /// </summary>
    void Update()
    {
        // Update values for the component.
        textComponent.text = "Hello! Time: " + Time.realtimeSinceStartup;
        textComponent.color = Random.ColorHSV();
        textComponent.fontSize = Random.Range(14, 32);
    }
}
  1. You can skip using GetComponent and assign references in the editor.

DO

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Good example, you can do it like that!
/// </summary>
public class DoThat : MonoBehaviour
{
    // Reference to the component which you can use in your code.
    [SerializeField]
    private Text textComponent;

    /// <summary>
    /// Set start values.
    /// </summary>
    void Start()
    {
        // Set start values for the component.
        textComponent.text = "Hello!";
        textComponent.color = Random.ColorHSV();
        textComponent.fontSize = Random.Range(14, 32);
    }

    /// <summary>
    /// Update values each frame.
    /// </summary>
    void Update()
    {
        // Update values for the component.
        textComponent.text = "Hello! Time: " + Time.realtimeSinceStartup;
        textComponent.color = Random.ColorHSV();
        textComponent.fontSize = Random.Range(14, 32);
    }
}

Just don’t forget to assign that reference!

Assigning reference through in the Unity Editor.
  1. For prefabs use reference to component rather than a GameObject.

DON’T

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Bad example, don't do it like that!
/// </summary>
public class DontDoThat : MonoBehaviour
{
    // Reference to the prefab as GameObject. Whenever you can you should use the type of specific component you are interested in.
    public GameObject textPrefab;

    /// <summary>
    /// Set start values.
    /// </summary>
    void Start()
    {
        CreatePrefabInstance();
    }

    /// <summary>
    /// Creating prefab instance.
    /// </summary>
    void CreatePrefabInstance()
    {
        // Creating prefab instance as GameObject
        var instance = Instantiate(textPrefab);

        // Continuously getting component. Don't do it.
        instance.GetComponent<Text>().text = "Hello!";
        instance.GetComponent<Text>().color = Random.ColorHSV();
        instance.GetComponent<Text>().fontSize = Random.Range(14, 32);
    }
}

DO

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Good example, you can do it like that!
/// </summary>
public class DoThat : MonoBehaviour
{
    // Reference to the prefab as Text component.
    [SerializeField]
    private Text textPrefab;

    /// <summary>
    /// Set start values.
    /// </summary>
    void Start()
    {
        CreatePrefabInstance();
    }

    /// <summary>
    /// Creating prefab instance.
    /// </summary>
    void CreatePrefabInstance()
    {
        // Creating prefab instance and receiving reference to the Text component.
        var textInstance = Instantiate(textPrefab);

        // Update values for the component.
        textInstance.text = "Hello!";
        textInstance.color = Random.ColorHSV();
        textInstance.fontSize = Random.Range(14, 32);
    }
}

And by using these few methods, you can save my (and probably others) sanity. ?

How about the performance

If this post didn’t convince you already, let’s speak about performance implications.

GetComponent method is resource-intensive. And those resources you can put into something else, like rendering more frames!

If you don’t believe me, you can go to my post about using Profiler and you can see it yourself.

I used the Transform component, which every GameObject has, and a run a quick test. I’ve called 100k times GetComponent<Transform>, transform property (underneath there is also GetComponent method), and cached transform property.

Result of running each method 100k times.

Can you see my problem here?

Summary? ?

Please apply these in your code. For my and your future sanity.

So that’s my rant about GetComponents in people’s code. I hope all of you take it to heart and won’t need it to do another one in the future! ?

Please share this post with someone you care about, and you want to help them make a better game!

What else people do that drives you insane? Let me know in the comment section below!

And if you want to be notified about future content on my blog, you can sign up for my newsletter!

Thanks for reading, and see you next time! ?

3.9 39 votes
Article Rating
Subscribe
Notify of
guest
13 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Daniel
Daniel
3 years ago

Great post Patryk!

I personally like to use

private Text something;
public Text Something => something ?? something = GetComponent<Text>()

because it works like Lazy, it only does GetComponent once only when object is being used and is pretty clean.

kuvasz013
kuvasz013
Reply to  Daniel
3 years ago

You still run the check every time you access the field, in exchange for a single line of code.

The shorthand looks cool, but it’s still better to store the ref during initialization.

Drew Lovejoy
Drew Lovejoy
Reply to  kuvasz013
3 years ago

If you are using the component once, then this is a good solution. However, it would get loud if you had more than one line of this.

Sean
Sean
3 years ago

Why no benchmarks? Seems like a necessity when comparing different methods based on their performance.

Asem
Asem
3 years ago

Awesome post.
Totally agree! , keep the good posts
??

Oleg
Oleg
3 years ago

Don’t use GetComponent inside Awake method. Start is better place for it.

Dimar
Dimar
Reply to  Oleg
3 years ago

Why is it? Never had any troubles in Awake

Huevito
Huevito
3 years ago

I just started learning Unity weeks ago, since now i’ll avoid the unnecessary get components like the plague

Drew Lovejoy
Drew Lovejoy
3 years ago

Usually, in C#, a private variable that is initialized on startup and then used in multiple methods needs an underscore in front of its name.

For example, textComponent would be _textComponent. It is an easy way to understand that the variable is not being created inside the method or being passed into the method.

I’m not sure how many people do this in the Unity community though.

Mbrk
Mbrk
3 years ago

Does it slow the code ? or just personal prefrence

frank
frank
3 years ago

Who does that… like really. I would imagine only noobs do this haha, but I chuckled 😀 thanks for that.

Tel Monks
Tel Monks
3 years ago

Have you looked at the actual code generated? I’ll bet the compiler takes care of the redundancy in the repeated calls to GetComponent.

13
0
Would love your thoughts, please comment.x
()
x