5
\$\begingroup\$

There are two main ways I am aware of to modify an attached component. Say, the component attached to an object is of type Animator and the name is animator.

So, in the script, I can write

private Animator animator;

and then call the function

animator = GetComponent<Animator>();

Or, I can define the variable as public

public Animator animator

and then, drag&drop in the Unity UI. Which one is the better practice?

\$\endgroup\$

2 Answers 2

8
\$\begingroup\$

There's a third option I sometimes like to use:

[RequireComponent(typeof(Animator))]
public class MyScript : MonoBehaviour {

    [SerializeField, HideInInspector]
    private Animator _animator;
    
    void OnValidate() {
        _animator = GetComponent<Animator>();
    }
}

Breaking this down...

  • RequireComponent ensures the object actually has the component I need, and Unity will add it at edit time if I forget it, or complain at me if I try to remove it.

  • Using both SerializeField and HideInInspector ensures we can save a value to the variable at edit time and have it save with the scene / carry through into game, without cluttering the Inspector with something we don't need to set manually.

  • OnValidate() gets called when we add or modify this component, only in the editor, so it can automatically wire up the reference for us without any search cost at runtime.

I wouldn't say this is "the best practice" — a public or serialized private variable is a lot more concise and does basically the same job, this just insulates you against forgetting to assign it. It's a little cheaper than finding the component on startup, but that cost isn't huge, so you can weigh whether the alternatives are worth the extra typing or hook-up. ;)


Another alternative you might like: Alex Hetherington has created a handy editor script that simplifies linking up component references. Now you can just add a [Resolve] attribute above the serialized component field you want to populate, and it will add a one-click button to populate the field (even populating an array of references), without you having to write your own initialization in OnValidate() or elsewhere. Here's a link to the GitHub repository with this and other utilities you can add to your project.

\$\endgroup\$
6
  • \$\begingroup\$ I'm a little confused, how is using [SerializeField, HideInInspector] any different than just not having either (excluding custom editors more complex things like that)? If it's hidden how are you setting values to it in edit? \$\endgroup\$
    – Jay
    Commented Apr 20, 2020 at 11:13
  • 4
    \$\begingroup\$ @Jake with this method, you don't manually assign the reference in the Inspector. The OnValidate method does it for you in code. That's why we hide it from the inspector. But in order for that reference that was assigned in code at edit time to be saved in the scene/prefab and survive into the built game, it needs to be serialized. Unity's serializer skips over private fields by default, so we need to tell it to save this one as an exception to the rule. \$\endgroup\$
    – DMGregory
    Commented Apr 20, 2020 at 11:15
  • \$\begingroup\$ Do you mean serailzeField will save the variable on the disk but hide in inspector will hide it for simplification? Hide in inspector is also overriding seralize field behaviour? \$\endgroup\$ Commented Feb 1, 2022 at 5:22
  • \$\begingroup\$ You could answer that question by trying it. [SerializeField] does not hide the variable from the inspector, so to hide it we also add [HideInInspector] \$\endgroup\$
    – DMGregory
    Commented Feb 1, 2022 at 12:03
  • 1
    \$\begingroup\$ Fortunately, the code above does not create any objects or call any non-thread-safe APIs. I've never experienced performance degradation in the editor when using it. You absolutely can check for null first, and use the newer TryGetComponent which skips some book-keeping work in the editor if it fails to find what it's looking for. \$\endgroup\$
    – DMGregory
    Commented Jun 12 at 10:19
3
\$\begingroup\$

First of all when you type

public Animator animator

what you're talking about is a SerializedField. You can do the same with private variables by doing

[SerializeField] private Animator animator

But as for your question the second one is better because it is faster. Using one or two GetComponent<>() calls generally isn't too bad, but if you use many in a single frame it could be a problem.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .