Overview
Tested with: Unity 2022.3 LTS / Unity 6
"I want to display private variables in the Inspector, but how?" "I want to add headings above fields to make the Inspector easier to read..."
This is where Attributes come in. Attributes are C# metadata markers that, when placed above class, property, or method declarations, instruct special behaviors.
[HideInInspector]
public float strength;
Inspector Attributes
SerializeField: Display Private Variables in the Inspector
[SerializeField]
private float speed = 5.0f;
This allows designers and planners to adjust values in the Inspector while maintaining encapsulation.
field: SerializeField: Serialize Properties
A modern way to display C# auto-properties in the Inspector.
[field: SerializeField] public float Speed { get; private set; } = 5.0f;
This provides a clean way to make a property read-only from code but editable in the Inspector.
HideInInspector: Hide Variables from the Inspector
[HideInInspector]
public float currentHealth;
The value is still serialized, so it is saved.
NonSerialized: Disable Serialization
[System.NonSerialized]
public float tempValue;
| Attribute | Serialized | Shown in Inspector |
|---|---|---|
HideInInspector | Yes | No |
NonSerialized | No | No |
When to use which: Use
NonSerializedfor temporary variables you don't want saved. UseHideInInspectorwhen you want the value saved but not editable in the Inspector.
Header: Display a Heading
[Header("Movement Settings")]
[SerializeField] private float moveSpeed = 5.0f;
[SerializeField] private float jumpForce = 10.0f;
[Header("Attack Settings")]
[SerializeField] private float attackPower = 20.0f;
Tooltip: Display a Tooltip
[Tooltip("Movement speed (units: m/s)")]
[SerializeField] private float moveSpeed = 5.0f;
Range: Display a Slider
[Range(0, 100)]
[SerializeField] private float volume = 50.0f;
Note:
Rangeonly restricts input in the Inspector. When assigning values directly from script, out-of-range values can still be set. UseMathf.Clampalongside if you need to enforce limits in code as well.
Min: Set a Minimum Value
[Min(0)]
[SerializeField] private float health = 100.0f;
Space: Add Spacing
[SerializeField] private float moveSpeed = 5.0f;
[Space(20)]
[SerializeField] private float attackPower = 20.0f;
TextArea: Multi-line Text Input
[TextArea(3, 10)]
[SerializeField] private string description;
Method Attributes
ContextMenu: Add Methods to the Inspector Context Menu
[ContextMenu("Restore Full Health")]
private void ResetHealth()
{
currentHealth = maxHealth;
Debug.Log("Health fully restored");
}
Right-click the component in the Inspector to see the menu item.
MenuItem: Add Methods to the Unity Editor Menu
Important: Scripts using
[MenuItem]must be placed inside an Editor folder. TheUnityEditornamespace is editor-only, so placing them outside the Editor folder will cause build errors.
// Editor/MyEditorTools.cs
using UnityEditor;
using UnityEngine;
public class MyEditorTools
{
[MenuItem("Tools/Reset All Objects")]
private static void ResetAllObjects()
{
Debug.Log("All objects have been reset");
}
}
RuntimeInitializeOnLoadMethod: Initialize at Game Startup
using UnityEngine;
public class GameInitializer
{
[RuntimeInitializeOnLoadMethod]
private static void Initialize()
{
Debug.Log("Game has started");
}
}
This can be used in static classes that do not inherit from MonoBehaviour.
Class Attributes
RequireComponent: Specify Required Components
[RequireComponent(typeof(Rigidbody))]
public class PlayerController : MonoBehaviour
{
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
}
When you attach PlayerController, a Rigidbody is automatically attached as well.
DisallowMultipleComponent: Prevent Duplicate Components
[DisallowMultipleComponent]
public class GameManager : MonoBehaviour
{
}
ExecuteAlways: Run in Editor Mode
[ExecuteAlways]
public class GridGenerator : MonoBehaviour
{
void Update()
{
// Separate logic for editor mode and play mode
if (Application.isPlaying)
{
// Runtime logic
}
else
{
// Editor logic (e.g., visualizing in Scene view)
}
}
}
Note: With
[ExecuteAlways],Updateis called every frame even in editor mode. Heavy operations will slow down the editor, so useApplication.isPlayingto branch logic or keep operations lightweight.
CreateAssetMenu: Add a ScriptableObject Creation Menu
[CreateAssetMenu(fileName = "NewWeapon", menuName = "MyGame/Weapon")]
public class WeaponData : ScriptableObject
{
public string weaponName;
public int damage;
}
Serialization Attributes
Serializable: Serialize Custom Classes
To display custom classes (that don't inherit from MonoBehaviour or ScriptableObject) in the Inspector, you need [Serializable].
[System.Serializable]
public class EnemyData
{
public string name;
public int hp;
public float speed;
}
public class EnemySpawner : MonoBehaviour
{
[SerializeField] private EnemyData[] enemies;
}
SerializeReference: Polymorphic Serialization
Allows you to hold instances of derived classes in base class or interface fields.
public interface ISkill
{
void Execute();
}
[System.Serializable]
public class FireSkill : ISkill
{
public float damage;
public void Execute() { /* Fire attack */ }
}
[System.Serializable]
public class IceSkill : ISkill
{
public float slowDuration;
public void Execute() { /* Ice attack */ }
}
public class Player : MonoBehaviour
{
[SerializeReference] private ISkill skill; // Can hold FireSkill or IceSkill
}
Note: When using
SerializeReference, selecting types in the Inspector requires a custom editor or a third-party asset like OdinInspector.
Custom Attributes
Defining a Custom Attribute
To create a custom Attribute that affects the Inspector, inherit from PropertyAttribute.
using UnityEngine;
[System.AttributeUsage(System.AttributeTargets.Field)]
public class ReadOnlyAttribute : PropertyAttribute
{
}
Note: You must inherit from
UnityEngine.PropertyAttribute, notSystem.Attribute. Inheriting fromSystem.Attributewill not work with PropertyDrawers.
Combining with Editor Extensions
PropertyDrawers must be placed inside an Editor folder.
Assets/
├── Scripts/
│ └── ReadOnlyAttribute.cs // Attribute definition
└── Editor/
└── ReadOnlyDrawer.cs // PropertyDrawer (inside Editor folder)
// Editor/ReadOnlyDrawer.cs
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
public class ReadOnlyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label);
GUI.enabled = true;
}
}
Important: Placing PropertyDrawers outside the Editor folder will cause build errors. The
UnityEditornamespace is editor-only and cannot be included in builds.
Practical Examples
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class PlayerController : MonoBehaviour
{
[Header("Movement Settings")]
[Tooltip("Movement speed (units: m/s)")]
[SerializeField] private float moveSpeed = 5.0f;
[Tooltip("Jump force")]
[SerializeField] private float jumpForce = 10.0f;
[Space(20)]
[Header("Attack Settings")]
[Range(10, 100)]
[SerializeField] private float attackPower = 20.0f;
[Min(0)]
[SerializeField] private float attackRange = 2.0f;
[Space(20)]
[Header("Status")]
[HideInInspector]
public float currentHealth;
[SerializeField] private float maxHealth = 100.0f;
[ContextMenu("Restore Full Health")]
private void ResetHealth()
{
currentHealth = maxHealth;
}
}
Best Practices
- Combine SerializeField with private - Maintain encapsulation while allowing Inspector adjustments
- Use Header and Space for readability - Group variables to improve Inspector organization
- Add Tooltip descriptions - Help team members understand each field
- Set appropriate ranges with Range and Min - Prevent incorrect value assignments
- Use RequireComponent for dependencies - Make component dependencies explicit
Summary
Attributes are C# metadata markers that instruct special behaviors in your code.
- SerializeField - Display private variables in the Inspector
- Header, Space - Improve Inspector readability
- Range, Min - Set value range restrictions
- RequireComponent - Auto-attach required components
- ContextMenu - Make methods executable from the editor
Use Attributes to write more readable and maintainable code.