Best practice for how often to use GetComponent?

I was silly and just tried to change the color of some text using some text by using this code:

private Color colorToChange= thisText.GetComponent<Text>().color;

colorToChange = new Color (r,g,b,a);

I know exactly what’s wrong with it, but I have a few questions which are kind of related.

Is there a way to reference the Text component (as opposed to the text object) so that I can avoid using GetComponent every frame?

Is GetComponent as “slow” as I’ve heard GameObject.Find is meant to be? (If it isn’t, don’t worry about the last question)

Is there ever a way to reference individual values so that you can change them directly i.e.

SomeDataType valueToChange = gameObject.GetComponent<Component>."value"

so that changing valueToChange in this script will directly change the value in the Component? Am I slipping over into global variables?

GetComponent iterates over each component on the selected GameObject until it finds one that matches the query- this is typically about a half dozen objects, or less. GetComponentInParent iterates over all components on the current GameObject, then all of the components on the parent GameObject, that that GameObject’s parent, etc… until it reaches the root. This can be dozens and dozens of components in total, but usually not more than a hundred. Likewise for GetComponentInChildren, but going in the other direction.

All of these checks are type comparisons, which are pretty fast, and the number of objects being iterated over are pretty small, but developers try to make a habit of “caching references” to various scripts if they need to be accessed more than once. You do this by defining a private field of the type of component you want to reference, then assigning the GetComponent result to that field in Awake or Start.

public class SomeComponent : MonoBehaviour
{
    private SomeOtherComponent someField;

    private void Awake()
    {
        someField = GetComponent<SomeOtherComponent>();
    }

    private void Start()
    {
        someField.someStringProperty = "New String";
    }
}

… or by making the field public, or adding the SerializeField attribute to it, so that it shows up as a reference in the inspector and you can just pick another component in the scene to use there.

GameObject.Find iterates over all active objects in the whole scene, doing string comparisons on the name of each. GameObject.FindWithTag is the same, but with the tag instead of the name, GameObject.FindGameObjectsWithTag is the same but returns all results in an array. String comparisons aren’t really cheap, especially when doing potentially hundreds or thousands of them at once, and “active GameObjects in the scene” tends to be a far larger number than “components in a given GameObject hierarchy”. Unless you’re making editor scripts, there are always more effective ways of keeping track of objects than doing scene searches, so I personally consider using GameObject.Find, or anything to do with tags, to be a failure in planning rather than a viable strategy.

For something resembling completion’s sake, since I’m going over most of the important search functions, Resources.FindObjectsOfTypeAll is a search by type instead of string, but it includes assets in the project files as well, so it could be thousands and thousands of results, making it even more painful than GameObject.Find unless the scene is massive.

Anyway, even the slowest of these operations isn’t going to be a big deal if you don’t do it in Update or a coroutine- do it once, and cache the results if you need to (as shown with GetComponent in the example), and you should be fine. If you need up-to-date references more often than that, then you’d be better off setting up a different system for obtaining it- updating the reference in a manager of some sort so that multiple scripts can access that value whenever they want, or setting up events to alert listeners when the reference changes, etc…

Your other questions are answered better by going through the beginner Scripting section on the Unity Learn site. I highly recommend sitting through all of the tutorials up through Intermediate before even touching Unity honestly- the GetComponent cached reference example above should show how to do most of what you were asking though.

2 Likes

Thanks a lot Lysander, this was pretty much what I was looking for, and I appreciate the time you put into it. I tried to go through the Unity official tutorials but in my opinion the only way you could possibly make them interesting is if you made them into a drinking game where you drank every time he said “go ahead and…”, and then I would only average one video per drunken stupor.

I think I have a decent handle on it now. Thanks again for your time.

That depends on SomeDataType! In C#, types fall into two distinct kinds; reference types (classes) and value types (structs and numbers). You can see explanations of that here.

When you get valueToChange, what you’re getting depends on wheter it’s a struct or a class. If it’s a struct (like Color or Vector3), you’ve retrieved a copy of it, and changing it only changes the copy. If it’s a class (Like some MonoBehaviour), you’re retrieved a reference to the same object, so you can change it and have the change visible on the other side.

1 Like

And one special case – arrays in unity are returned as copies:)

1 Like

Thanks Baste :slight_smile: