We make games that say something new.
Hide in Inspector

Hide in Inspector

Editor scripting in Unity is one of the more powerful tools available for improving workflow. It’s great to be able to write my own editor window to add new features to the editor, but I’m in love with the flexibility that I get from custom property drawers. Unity has a few built in examples that you’re probably familiar with: SerializeField, Range, and of course, HideInInspector. Simple attributes that can be applied to any field that have a dramatic effect on how those fields get used. Want a field to be private, but editable in the inspector? SerializeField. Public, but not editable? HideInInspector. So helpful!

But of course, there are plenty of other scenarios in which I might want to hide things in the editor. Let’s say I have a class with several fields to configure:

public class ConfigurableObject : MonoBehaviour
{
    public string greeting;

    public bool isBadGuy;

    public string badGuyGreeting;

    [SerializeField]
    private string currentThoughts;
}

There’s one field that every implementation will use, one that flags an implementation as a certain specialization, and one that applies only to the specialization. There’s also a private field that is only being serialized so it will show up in the inspector (I’ll get to that later).

Unfortunately, this means I’m stuck looking at some fields that are only sometimes relevant:

hideunless0

What I’d love is an attribute that would hide a field unless it was needed. Something like this:

// --------------------------------
// <copyright file="HideUnlessAttribute.cs" company="Rumor Games">
//     Copyright (C) Rumor Games, LLC.  All rights reserved.
// </copyright>
// --------------------------------

using UnityEngine;

/// <summary>
/// HideUnlessAttribute class.
/// </summary>
public class HideUnlessAttribute : PropertyAttribute
{
    /// <summary>
    /// Initializes a new instance of the HideUnlessAttribute class.
    /// </summary>
    /// <param name="fieldName">Field that controls whether this property is hidden or visible.</param>
    public HideUnlessAttribute(string fieldName)
    {
        this.FieldName = fieldName;
    }

    /// <summary>
    /// Gets the name of the field this attribute's drawer will test to see whether to show in the inspector.
    /// </summary>
    public string FieldName { get; private set; }
}

This attribute allows me to mark a field as depending on another (boolean) field to be true in order to show in the inspector, perfect for hiding certain variables until they’re needed. But of course, the attribute does nothing without a paired drawer to make the magic happen:

// --------------------------------
// <copyright file="HideUnlessAttributeDrawer.cs" company="Rumor Games">
//     Copyright (C) Rumor Games, LLC.  All rights reserved.
// </copyright>
// --------------------------------

using UnityEditor;
using UnityEngine;

/// <summary>
/// HideUnlessAttributeDrawer class.
/// </summary>
[CustomPropertyDrawer(typeof(HideUnlessAttribute))]
public class HideUnlessAttributeDrawer : HideableAttributeDrawer
{
    /// <summary>
    /// Gets the HideUnlessAttribute to draw.
    /// </summary>
    private HideUnlessAttribute Attribute
    {
        get
        {
            return (HideUnlessAttribute)this.attribute;
        }
    }

    /// <summary>
    /// Checks whether the property is supposed to be hidden.
    /// </summary>
    /// <param name="property">The SerializedProperty to test.</param>
    /// <returns>True if the property should be hidden.</returns>
    protected override bool IsHidden(SerializedProperty property)
    {
        var attributeProperty = property.serializedObject.FindProperty(this.Attribute.FieldName);
        if (attributeProperty.propertyType == SerializedPropertyType.Boolean)
        {
            return !attributeProperty.boolValue;
        }

        Debug.LogWarning("HideUnless attribute is referencing an invalid field name: " + this.Attribute.FieldName);
        return false;
    }
}

Now I can mark one field as depending on another to be shown:

    [HideUnless("isBadGuy")]
    public string badGuyGreeting;

Unchecked and hidden:

hideunless1

Checked and visible:

hideunless2

Now, most of the important and necessary PropertyDrawer stuff is noticeably absent in the HideUnlessAttributeDrawer. That’s because, since hiding things is a pretty common problem, I abstracted the general functionality into a reusable base class:

// --------------------------------
// <copyright file="HideableAttributeDrawer.cs" company="Rumor Games">
//     Copyright (C) Rumor Games, LLC.  All rights reserved.
// </copyright>
// --------------------------------

using UnityEditor;
using UnityEngine;

/// <summary>
/// HideableAttributeDrawer class.
/// </summary>
public abstract class HideableAttributeDrawer : PropertyDrawer
{
    /// <summary>
    /// Draws the GUI for the property.
    /// </summary>
    /// <param name="position">Rectangle on the screen to use for the property GUI.</param>
    /// <param name="property">The SerializedProperty to make the custom GUI for.</param>
    /// <param name="label">The label of this property.</param>
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        if (!this.IsHidden(property))
        {
            EditorGUI.PropertyField(position, property, label);
        }
    }

    /// <summary>
    /// Get the property height in pixels of the given property.
    /// </summary>
    /// <param name="property">The SerializedProperty to get height for.</param>
    /// <param name="label">The label of this property.</param>
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return this.IsHidden(property) ? 0f : base.GetPropertyHeight(property, label);
    }

    /// <summary>
    /// Checks whether the property is supposed to be hidden.
    /// </summary>
    /// <param name="property">The SerializedProperty to test.</param>
    /// <returns>True if the property should be hidden.</returns>
    protected abstract bool IsHidden(SerializedProperty property);
}

Now I can write any number of drawers that implement their own IsHidden class, rendering their associated field when a certain condition is met. Which brings me back to that private field, currentThoughts. I only care about it when the game is running. There’s no reason for it to show up in the inspector when I’m editing, and in fact, allowing me to edit it can be misleading and even introduce errors. That’s why I made this bonus script to hide a field when editing:

// --------------------------------
// <copyright file="HideInEditModeAttribute.cs" company="Rumor Games">
//     Copyright (C) Rumor Games, LLC.  All rights reserved.
// </copyright>
// --------------------------------

using UnityEngine;

/// <summary>
/// HideInEditModeAttribute class.
/// </summary>
public class HideInEditModeAttribute : PropertyAttribute
{
}
// --------------------------------
// <copyright file="HideInEditModeAttributeDrawer.cs" company="Rumor Games">
//     Copyright (C) Rumor Games, LLC.  All rights reserved.
// </copyright>
// --------------------------------

using UnityEditor;
using UnityEngine;

/// <summary>
/// HideInEditModeAttributeDrawer class.
/// </summary>
[CustomPropertyDrawer(typeof(HideInEditModeAttribute))]
public class HideInEditModeAttributeDrawer : HideableAttributeDrawer
{
    /// <summary>
    /// Checks whether the property is supposed to be hidden.
    /// </summary>
    /// <param name="property">The SerializedProperty to test.</param>
    /// <returns>True if the property should be hidden.</returns>
    protected override bool IsHidden(SerializedProperty property)
    {
        return !Application.isPlaying;
    }
}

In action:

    [HideInEditMode]
    [SerializeField]
    private string currentThoughts;

Now, it’s completely hidden in edit mode:

hideunless3

…but visible in play mode!

These scripts are pretty simple, but make a big difference. When the editor feels like it’s working with me, supporting my workflow and reacting to my wants and needs, I’m happier and more productive. And that’s a pretty great reason to set aside a little bit of dev time for editor scripting.

2 Comments

  1. Jiwan

    Hi, thanks for the inspiring post, but I’m getting the following error when I tried your scripts:

    error CS0246: The type or namespace name `HideUnless’ could not be found. Are you missing a using directive or an assembly reference?

    Can you please help me with this?

    1. Hello Jiwan, that error indicates Unity can’t find the HideUnlessAttribute class in your assets. In order to use the scripts, you’ll need to “Create > C# Script” for each file you want to use (at minimum, HideUnlessAttribute.cs and HideUnlessAttributeDrawer.cs), and then copy the contents of the scripts in this post into the files, replacing any boilerplate code. If you did that, you might want to double-check that there aren’t any compiler errors that are preventing Unity from recognizing the new classes.

Comments are closed.