T.TAO
Back to Blog
/4 min read/Game Engine

Unity Inspector #1 Basic Operations

#Unity#GameEngine#CSharp

These notes are about editing skills for writing a more readable, better organized script in Unity. The Inspector is the interface your designers and artists use all day; an hour spent on it repays itself many times over.

Exposing fields without making them public

In a MonoBehaviour you can declare many types of public variable and set most of them in the Inspector.

C#public int age;

The problem is that public also means any other script holding a reference can read and write the field. Usually you want the Inspector convenience without the access. Declare the field private and add a [SerializeField] attribute:

C#[SerializeField] private int age;

The field still shows up in the Inspector, but it stays private to other code.

The inverse also exists. A public field that should not be serialized — because you assign it at runtime, or because serializing it would be meaningless — gets [NonSerialized]:

C#[System.NonSerialized] public Transform runtimeTarget;

And [HideInInspector] public int index; keeps a field public and serialized but hides the row, which is useful for values another script writes and nobody should hand-edit.

Attributes that shape the layout

Attributes in square brackets are what regulate how the script appears. These are the ones worth memorizing:

C#[Header("Movement")]                          // a bold section title with a gap above it
[Tooltip("Units per second at full input.")]  // text shown on hover
[Range(1, 10)]                                // a slider instead of a number box
[Min(0)]                                      // clamps the lower bound
[TextArea(3, 10)]                             // multi-line text box, min and max rows
[Space(12)]                                   // vertical gap in pixels
[SerializeField] private float moveSpeed = 5f;

[Header] and [Space] cost nothing and do more for readability than any other change. A component with twenty ungrouped fields is unusable; the same twenty fields under four headers are fine.

Two more that show up constantly:

C#[RequireComponent(typeof(Rigidbody))]  // on the class: Unity adds the dependency automatically
public class Mover : MonoBehaviour { }

[ContextMenu("Reset Path")]            // on a method: run it from the component's ⋮ menu
private void ResetPath() { }

[RequireComponent] is a small thing that prevents a whole class of null-reference bug: the component can no longer exist without its dependency, and Unity will refuse to let you remove the Rigidbody while the Mover is attached.

[ContextMenu] is the cheapest possible tool: any editor-time action you would otherwise write a custom editor for — regenerating data, snapping to the ground, clearing a cache — can be a private method with this attribute.

Conditional and validated fields

OnValidate runs in the editor whenever a serialized value changes. It is the right place to keep derived state consistent, and to catch a bad value at authoring time rather than at runtime:

C#private void OnValidate()
{
    if (segmentCount < 1) segmentCount = 1;
    if (mesh != null && mesh.isReadable == false)
        Debug.LogWarning($"{name}: mesh is not readable", this);
}

Keep OnValidate cheap and side-effect-free outside the component itself. It is called more often than you think, including on domain reload and prefab instantiation.

When to write a custom editor

The attributes above cover most cases. Reach for [CustomEditor] or a PropertyDrawer only when you need something they genuinely cannot express:

  • a field whose visibility depends on another field's value,
  • a scene-view handle for editing a position or radius directly,
  • a preview, a validation summary, or a button that does real work.

A PropertyDrawer is usually the better choice: it targets one serializable type and works everywhere that type appears, including inside lists, whereas a CustomEditor replaces the whole component's inspector and has to be maintained as the component grows.

The rule I follow: if I am writing editor code to save myself clicks, the attribute version is almost always enough. If I am writing it so that someone else does not make a mistake, the custom drawer is worth it.

In this series

Unity Engine
  1. 01Unity Engine #0 C#
  2. 02Unity Engine #1 Memory
  3. 03Unity Engine #2 System & Networking
  4. 04Unity Engine #3 Design Patterns
  5. 05Unity Engine #4 MonoBehaviour
  6. 06Unity Engine #5 Physics
  7. 07Unity Inspector #1 Basic Operations