object referencing itself instead of using GetComponent<>?

In terms of working within 1 object, I’ve always used GetComponent to change things like spriterenderer colors, rigidbody2d velocity etc. But I realized I could just make a [SerializedField] with the component type and reference the object itself. Would this be a good strategy to cut down on GetComponent calls or am I overthinking things?

1 Like

Sure. People do this all the time. It’s more about convenience than optimization. calling GetComponent in your Start function is no big deal.

2 Likes

Yep it is a good strategy. A public field or private with serializedfield. Since usually the GetComponent calls are in Start, it cuts down on any lag when the object is instantiated.

Yes, its definately better than calling GetComponent<>, which is pretty slow.

If you find yourself needing to call GetComponent at runtime, i.e to change a script reference to a script on another object, or some other reason, you’re better off caching each component into a variable.

An even better solution would be to subscribe to an event that gets fired if a value in a component changes, or have the component subscribe any method calls fired by your original script.

No.

Because you replace something that happens automatically, with something that requires a manual action that you will forget to do. It will work, yes. But getting the component within OnEnable() and caching it is a better way.

So, make a non-serialized field, assign component to it from within Start(), Awake() or OnEnable(), and use that variable afterwards. [SerializeField] members are the ones you assign through inspector.

2 Likes

I can see the advantage of using both.

Manually assigning component reference would be better than using GetComponent<> at Start(), but it’s manual work and chances are you might forget to assign components to it.

But, if you’re using prefabs, chances are you won’t be doing this manual assignment too often.

It’s a shame GetComponent<> is so slow. Why can’t Unity generate some HashSet/Dictionary at runtime with all component references in there? This would make it much faster to access all components. Same with searching for GameObjects within a scene.

2 Likes

Agreed, and there is a way to do both without having to worry about forgetting to hook up the reference.

public Foo foo;

void Awake()
{
    if (foo == null) foo = GetComponent<Foo>();
}
1 Like

I wouldn’t call that “doing both”. It’s more of a fallback to a latter method if somebody forgets to attach a reference.

If you forget to attach a reference, then it’s going to use expensive GetComponent<Foo> operation and nobody would know about it unless manually checked, which is the worst-case scenario.

I would rather prefer I was shown a Null Reference exception error so I would know I messed up and forgot attaching a reference.

My personal beef with manually assigning references is that it clogs the inspector and there’s no way to hide those fields unless you’re using Odin Inspector or your own inspector drawer.

It’s even more ridiculous if you need references to a neighbouring component as you have to manually specify references to Components that are part of the same GameObject!

Speaking of which I wasn’t able to find performance benchmarks when I searched for them. I’m fairly confident it isn’t as slow as everyone is making it out to be in this thread. Knowing my luck lately they’re buried in a video from a past Unite.

2 Likes

Hmm, they might’ve optimized it behind the scenes. It’s impossible to tell because decompiled DLL of UnityEngine.dll doesn’t tell me a lot since it does an external call (probably into native part of the engine). One way is to do a proper benchmark between Unity versions, would be an interesting project to undertake.

I would still cache GetComponent return values purely because it does external calls.

Honestly, using GetComponent at Start is not that bad. Yes, it might be a bit slow when GameObjects get instantiated, but you tend to use object pooling in most cases anyway.

My general rules of thumb:
1 - If I can reliably make the code get the correct component itself then I will do that. This reduces work when assembling content and eliminates certain types of errors, eg: forgetting to hook something up, hooking the wrong thing up, things like that.
2 - If I can’t reliably make it get the right thing, or if the reference is based on a design / implementation decision rather than something algorithmic, then it goes in the Inspector.

Basically, the Inspector is for when I need a human in the loop, but I want to keep humans out of the loop as much as reasonably possible.

Edit: Note that if performance at startup is a confirmed problem in your case then you can approach that as an optimisation. Mark fields as both [SerializeField] and [HideInInspector] so that they get serialized but are hidden from humans, and if null initialise the relevant components in OnValidate(). That way you still get the performance benefit of the references being hooked up at edit time, and the productivity benefit of a human not having to do the work. But, I wouldn’t complicate things like that unless I knew I had to.

1 Like