Back to Blog

15 Essential C# Attributes in Unity

Enes Efe Tokta

Enes Efe Tokta

Feb 12, 2024 • 8 min read

Unity C# Attributes Tutorial

When programming Unity games with C#, we sometimes notice that the variables we define can make the Inspector screen cluttered and disorganized. C# Attributes—one of the language's powerful features—help make our variables more readable and keep our Inspector clean.

What Are Attributes?

While Attributes are commonly used to keep the Inspector clean, that's not their only purpose. They can control how your code runs under specific conditions and serve several other important functions in Unity development.

Let's explore 15 essential C# Attributes that every Unity developer should know.

1 [SerializeField]

Perhaps the most popular attribute. It allows you to control private or protected variables from the Unity Inspector without making them public.

public class Player : MonoBehaviour 
{
    [SerializeField]
    private float health = 100f;
    
    [SerializeField]
    private int maxAmmo = 30;
}

Use Case

Use this for variables you want to adjust in the Inspector but don't want other scripts to access directly. This maintains encapsulation while allowing designer-friendly tweaking.

2 [Range(min, max)]

Controls the minimum and maximum values a variable can take, creating a slider in the Inspector.

public class PlayerController : MonoBehaviour 
{
    [Range(1f, 10f)]
    public float moveSpeed = 5f;
    
    [Range(0f, 1f)]
    public float damageMultiplier = 0.5f;
}

Use Case

Perfect for values that should stay within specific bounds, like percentages (0-1), speeds, or difficulty multipliers. The slider makes it easy to fine-tune values visually.

3 [Header("Title")]

Adds section headers to organize variables in the Inspector, making it easier to find and group related fields.

public class UIManager : MonoBehaviour 
{
    [Header("Text Components")]
    public Text scoreText;
    public Text healthText;
    
    [Header("Image Components")]
    public Image playerAvatar;
    public Image enemyIcon;
}

Use Case

Essential for scripts with many fields. Group related variables under descriptive headers to create a cleaner, more organized Inspector.

4 [Tooltip("Description")]

Displays a helpful message when you hover over a variable in the Inspector, explaining its purpose.

public class GameSettings : MonoBehaviour 
{
    [Tooltip("Maximum number of enemies that can spawn simultaneously")]
    public int maxEnemies = 10;
    
    [Tooltip("Time in seconds between enemy spawn waves")]
    public float spawnInterval = 5f;
}

Use Case

Invaluable for team collaboration. Document what each variable does directly in the Inspector, helping other developers (or future you) understand the purpose at a glance.

5 [RequireComponent(typeof(Component))]

Automatically adds required components to a GameObject, ensuring dependencies are always present.

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class PlayerMovement : MonoBehaviour 
{
    private Rigidbody rb;
    
    void Start() 
    {
        rb = GetComponent();
    }
}

Use Case

Prevents runtime errors by guaranteeing required components exist. Unity will automatically add them when you attach your script to a GameObject.

6 [ExecuteInEditMode]

Runs a MonoBehaviour script in Edit Mode, allowing code execution without pressing Play.

[ExecuteInEditMode]
public class ProceduralMeshGenerator : MonoBehaviour 
{
    void Update() 
    {
        // This runs even in Edit Mode
        GenerateMesh();
    }
}

Use Case

Useful for editor tools, procedural generation previews, or real-time position gizmos. Be careful—this runs constantly in the editor, so optimize accordingly.

7 [HideInInspector]

The opposite of [SerializeField]. Hides public variables from the Inspector while keeping them accessible to other scripts.

public class DataManager : MonoBehaviour 
{
    [HideInInspector]
    public int internalCounter = 0;
    
    public string visibleData = "Hello";
}

Use Case

Hide variables that need to be public for script communication but shouldn't be modified in the Inspector (like runtime counters or internal state).

8 [Obsolete("Message")]

Marks methods or variables as deprecated, warning developers when they use outdated code.

public class LegacySystem : MonoBehaviour 
{
    [Obsolete("Use NewAttackMethod() instead")]
    public void OldAttackMethod() 
    {
        // Old implementation
    }
    
    public void NewAttackMethod() 
    {
        // New implementation
    }
}

Use Case

Essential for code maintenance. Guide developers to updated methods while keeping legacy code functional during refactoring. IDEs will show a warning when using obsolete code.

9 [ExecuteAlways]

Similar to [ExecuteInEditMode], but also runs when you press Play. More versatile for tools that need to work in both modes.

[ExecuteAlways]
public class DebugGizmo : MonoBehaviour 
{
    void Update() 
    {
        // Runs in both Edit Mode and Play Mode
        DrawDebugInfo();
    }
}

Use Case

Perfect for debug visualization tools, editor extensions, or systems that need to update continuously regardless of play state.

10 [ContextMenu("Menu Name")]

Adds a custom menu item to the Inspector's context menu, allowing you to execute methods with a right-click.

public class HealthSystem : MonoBehaviour 
{
    public float health = 100f;
    
    [ContextMenu("Reset Health")]
    private void ResetHealth() 
    {
        health = 100f;
        Debug.Log("Health reset to 100");
    }
    
    [ContextMenu("Take Damage")]
    private void DebugDamage() 
    {
        health -= 25f;
    }
}

Use Case

Extremely useful for testing and prototyping. Quickly trigger methods without writing temporary test code or custom editor buttons.

11 [AddComponentMenu("Path")]

Customizes where your script appears in the Component menu, making it easier to find.

[AddComponentMenu("Custom/Player/Movement System")]
public class PlayerMovement : MonoBehaviour 
{
    // Component will appear under:
    // Component → Custom → Player → Movement System
}

Use Case

Organize your custom components into logical categories. Especially helpful in large projects with many custom scripts.

12 [Space(pixels)]

Adds vertical spacing in the Inspector, helping visually separate groups of variables.

public class WeaponStats : MonoBehaviour 
{
    public int damage = 10;
    public float fireRate = 0.5f;
    
    [Space(20)]
    public int ammo = 30;
    public int maxAmmo = 120;
}

Use Case

Simple but effective for improving Inspector readability. Use alongside [Header] for maximum organization.

13 [TextArea(minLines, maxLines)]

Expands the text input field in the Inspector, perfect for longer text content.

public class DialogueSystem : MonoBehaviour 
{
    [TextArea(3, 10)]
    public string dialogueText = "Enter your dialogue here...";
    
    public string normalText = "Short text";
}

Use Case

Essential for dialogue systems, item descriptions, or any text that spans multiple lines. Makes editing much easier than a tiny single-line field.

14 [Multiline(lines)]

Similar to [TextArea], but takes only one parameter specifying the number of lines.

public class QuestSystem : MonoBehaviour 
{
    [Multiline(5)]
    public string questDescription;
    
    [Multiline(3)]
    public string questObjective;
}

Use Case

Use when you know exactly how many lines you need. Slightly simpler than [TextArea] for fixed-size text fields.

15 [DisallowMultipleComponent]

Prevents the same component from being added multiple times to a GameObject.

[DisallowMultipleComponent]
public class PlayerController : MonoBehaviour 
{
    // Only one PlayerController can exist per GameObject
    // Unity will prevent adding a second instance
}

Use Case

Prevents errors from duplicate singleton components, managers, or controllers. Useful for any component where multiple instances would cause conflicts.

Combining Attributes

The real power of attributes comes from combining them. Here's a practical example:

[RequireComponent(typeof(Rigidbody))]
[DisallowMultipleComponent]
public class AdvancedMovement : MonoBehaviour 
{
    [Header("Movement Settings")]
    [Tooltip("Maximum speed the player can achieve")]
    [Range(1f, 20f)]
    public float maxSpeed = 10f;
    
    [Space(10)]
    
    [Header("Jump Settings")]
    [Tooltip("Upward force applied when jumping")]
    [Range(1f, 30f)]
    public float jumpForce = 15f;
    
    [SerializeField]
    [Tooltip("Can the player jump multiple times in the air?")]
    private bool allowDoubleJump = false;
    
    [HideInInspector]
    public bool isGrounded = true;
    
    [ContextMenu("Reset to Default")]
    private void ResetToDefault() 
    {
        maxSpeed = 10f;
        jumpForce = 15f;
        allowDoubleJump = false;
    }
}

Best Practices

Tips for Using Attributes Effectively

  • Always use [Tooltip] for non-obvious variables
  • Organize with [Header] in scripts with 5+ public variables
  • Validate with [Range] for numerical constraints
  • Protect with [SerializeField] instead of making everything public
  • Document with [Obsolete] when refactoring old code
  • Test with [ContextMenu] for quick debugging
  • Enforce with [RequireComponent] to prevent missing dependencies

Additional Resources

To learn more about Unity Attributes, check out these resources:

Conclusion

C# Attributes are essential tools for professional Unity development. They help you:

  • Keep your Inspector organized and readable
  • Enforce code constraints automatically
  • Improve team collaboration with better documentation
  • Speed up prototyping and debugging
  • Maintain cleaner, more maintainable code

Master these 15 attributes, and you'll write cleaner, more professional Unity code. Start incorporating them into your projects today!