Draw a field only if a condition is met

Until now when I wanted to make a field appear in the inspector I made a custom inspector, but I got tired of it, so I decided to make an attribute.

The files needed:
1. First of all, I used something that I posted about in another please. You can find it here, get it before you get the attribute.
2. Put this enum wherever you want:

/// <summary>
/// Types of comperisons.
/// </summary>
public enum ComparisonType
{
    Equals = 1,
    NotEqual = 2,
    GreaterThan = 3,
    SmallerThan = 4,
    SmallerOrEqual = 5,
    GreaterOrEqual = 6
}

3. Put this enum wherever you want as well:

/// <summary>
/// Types of comperisons.
/// </summary>
public enum DisablingType
{
    ReadOnly = 2,
    DontDraw = 3
}

4. Put this inside of a CSharp file wherever you want in your project:

using UnityEngine;
using System;

/// <summary>
/// Draws the field/property ONLY if the copared property compared by the comparison type with the value of comparedValue returns true.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class DrawIfAttribute : PropertyAttribute
{
    public string comparedPropertyName { get; private set; }
    public object comparedValue { get; private set; }
    public ComparisonType comparisonType { get; private set; }
    public DisablingType disablingType { get; private set; }

    /// <summary>
    /// Only draws the field only if a condition is met.
    /// </summary>
    /// <param name="comparedPropertyName">The name of the property that is being compared (case sensitive).</param>
    /// <param name="comparedValue">The value the property is being compared to.</param>
    /// <param name="comparisonType">The type of comperison the values will be compared by.</param>
    /// <param name="disablingType">The type of disabling that should happen if the condition is NOT met. Defaulted to DisablingType.DontDraw.</param>
    public DrawIfAttribute(string comparedPropertyName, object comparedValue, ComparisonType comparisonType, DisablingType disablingType = DisablingType.DontDraw)
    {
        this.comparedPropertyName = comparedPropertyName;
        this.comparedValue = comparedValue;
        this.comparisonType = comparisonType;
        this.disablingType = disablingType;
    }
}

5. Put this wherever you want, just make sure it’s either directly or indirectly in a folder called “Editor”:

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(DrawIfAttribute))]
public class DrawIfPropertyDrawer : PropertyDrawer
{
    // Reference to the attribute on the property.
    DrawIfAttribute drawIf;

    // Field that is being compared.
    SerializedProperty comparedField;

    // Height of the property.
    private float propertyHeight;

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return propertyHeight;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Set the global variables.
        drawIf = attribute as DrawIfAttribute;
        comparedField = property.serializedObject.FindProperty(drawIf.comparedPropertyName);

        // Get the value of the compared field.
        object comparedFieldValue = comparedField.GetValue<object>();

        // References to the values as numeric types.
        NumericType numericComparedFieldValue = null;
        NumericType numericComparedValue = null;

        try
        {
            // Try to set the numeric types.
            numericComparedFieldValue = new NumericType(comparedFieldValue);
            numericComparedValue = new NumericType(drawIf.comparedValue);
        }
        catch (NumericTypeExpectedException)
        {
            // This place will only be reached if the type is not a numeric one. If the comparison type is not valid for the compared field type, log an error.
            if (drawIf.comparisonType != ComparisonType.Equals && drawIf.comparisonType != ComparisonType.NotEqual)
            {
                Debug.LogError("The only comparsion types available to type '" + comparedFieldValue.GetType() + "' are Equals and NotEqual. (On object '" + property.serializedObject.targetObject.name + "')");
                return;
            }
        }

        // Is the condition met? Should the field be drawn?
        bool conditionMet = false;

        // Compare the values to see if the condition is met.
        switch (drawIf.comparisonType)
        {
            case ComparisonType.Equals:
                if (comparedFieldValue.Equals(drawIf.comparedValue))
                    conditionMet = true;
                break;

            case ComparisonType.NotEqual:
                if (!comparedFieldValue.Equals(drawIf.comparedValue))
                    conditionMet = true;
                break;

            case ComparisonType.GreaterThan:
                if (numericComparedFieldValue > numericComparedValue)
                    conditionMet = true;
                break;

            case ComparisonType.SmallerThan:
                if (numericComparedFieldValue < numericComparedValue)
                    conditionMet = true;
                break;

            case ComparisonType.SmallerOrEqual:
                if (numericComparedFieldValue <= numericComparedValue)
                    conditionMet = true;
                break;

            case ComparisonType.GreaterOrEqual:
                if (numericComparedFieldValue >= numericComparedValue)
                    conditionMet = true;
                break;
        }

        // The height of the property should be defaulted to the default height.
        propertyHeight = base.GetPropertyHeight(property, label);
   
        // If the condition is met, simply draw the field. Else...
        if (conditionMet)
        {
            EditorGUI.PropertyField(position, property);
        }
        else
        {
            //...check if the disabling type is read only. If it is, draw it disabled, else, set the height to zero.
            if (drawIf.disablingType == DisablingType.ReadOnly)
            {
                GUI.enabled = false;
                EditorGUI.PropertyField(position, property);
                GUI.enabled = true;
            }
            else
            {
                propertyHeight = 0f;
            }
        }
    }
}

This is how you can use it:
This is just an example script I put on one of the game objects:

using UnityEngine;

public class Test : MonoBehaviour
{
    [Range(0, 2)]
    public float someFloat;

    [DrawIf("someFloat", 1f, ComparisonType.GreaterOrEqual)]
    public int someInt;

    public bool someBool;

    [DrawIf("someBool", true, ComparisonType.Equals, DisablingType.ReadOnly)]
    public int anotherInt;
}

This is the result:

Just keep in mind a few things:

  • When the condition isn’t met and it doesn’t draw the field, when it is met and it draws it it will take a very little bit of time to update the height (you can see it in the GIF).
  • When you spell the name of the compared field incorrectly, it won’t give you an error. So make sure the spelling is correct and the capital letters are correct as well (it’s case sensitive).

EDIT:
I originally wrote “comperison”, instead of “comparison”. I tried to fix it in all of the places, but if anyone found any more mistakes (since I think I missed some), please tell me.

For anyone who doesn’t want to copy the code, you download it here. Just get the “Hide If Attribute” folder. You can also try the numeric type one: Free Assets - Google Drive

18 Likes

First of all, Comparison
Then wouldn’t it be better if you posted it as a single cs file and told us, that it needs to be in thEditor folder?

Ugh, I knew it was comparison… For some reason I just went with e. I don’t know why, I was probably just dumb. I will fix it.
And what doesn the second line mean? I don’t get it.

1 Like

It means, that instead of telling us to copy a bunch of code somewhere in our project, you should put the whole thing into a c# file and post that instead

Really nice!

I’ve got the same thing going in our project (think I’ve posted it earlier), though I use a “HideIf” attribute. Since showing is default, I guess I fealt that the attribute that you put on should have the name of what you’re changing it to? idk.

I’ve also split the different kinds of attributes into different attribute types. So a hider that checks a bool (HideIf) has a different type from a hider that checks for objects (ie. HideIfNull/HideIfNotNull) and the hider for enum values (HideIfEnumValue). I’ve never had the need for number-based comparrison.

I did that because they’ve got different needs. The bool one needs to check either against true of false, while the Enum one checks against an array of possible values.

I really like the disableType, I’ll probably grab that. I’ve just been completely hiding things, didn’t think of the disabling possibility.

The height changing is a shame, but nothing we can do anything about. As far as I can tell, the editor is drawn in two passes - the first pass calculates the position of everything, while the second does the drawing and the input. The problem is that your input can change the required height, but that’s not handled in the layout phase.

@gorbit99 , don’t be lazy! Copy-pasting ain’t that hard. Having the code in the post is also a lot nicer for people who are super-paranoid about downloading stuff from posts.

4 Likes

@Baste I’m not lazy, I’m not even planning on using this

Hmm…sure. I actually thought about doing it but I was too lazy to do it, since I thought most people don’t talk.
Anyways, I edited the post. You can download it now.

Thanks.

I don’t really know… “DrawIf” just popped in my mind. I don’t think I will change that.

I didn’t split the different kinds of attributes because it feels kinda inefficient. It did actually get me into a lot of trouble to only use one attribute, but it ended up giving me that NumericType thing, which I really like. And it’s more efficient.
I actually only had the need to use a boolean in my project, the others are just in case later I will use them.

Line 2 of the first and second code and line 20 in the third still contains a comperison

Updated, hopefully I finally fixed all of the mistakes.

@Ov-Aviram - Great job.

I know it is a while ago, but I was actually interested in using this script. It didn’t work for me since I was missing the GetValue extension method for serializedproperties. I made some modifications to the script so that it just checks and shows the properties when equal to bool and enums, which is what I needed. I also circumvented the height issue.

Example
For those of you who are interested in a property drawer that can hide and show elements like so:

   public enum ShowValueEnum
    {
        ShowValue1,
        ShowValue2,
        None
    }

    [Header("Show Value?")]
 
    // An example with an enum
    public ShowValueEnum EnumTest = ShowValueEnum.None;
    [DrawIf("EnumTest", ShowValueEnum.ShowValue1)]  //Show if enum is equal to ShowValue1
    public int Value1 = 100;
    [DrawIf("EnumTest", ShowValueEnum.ShowValue2)]  //Show if enum is equal to ShowValue2
    public int Value2 = 200;

    // An example for a bool
    public bool BoolTest = false;
    [DrawIf("BoolTest", true)] // Show if booltest bool is true
    public Vector3 Value;

Attribute script:

using UnityEngine;
using System;

/// <summary>
/// Draws the field/property ONLY if the compared property compared by the comparison type with the value of comparedValue returns true.
/// Based on: https://discussions.unity.com/t/650579
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class DrawIfAttribute : PropertyAttribute
{
    #region Fields

    public string comparedPropertyName { get; private set; }
    public object comparedValue { get; private set; }
    public DisablingType disablingType { get; private set; }

    /// <summary>
    /// Types of comperisons.
    /// </summary>
    public enum DisablingType
    {
        ReadOnly = 2,
        DontDraw = 3
    }

    #endregion

    /// <summary>
    /// Only draws the field only if a condition is met. Supports enum and bools.
    /// </summary>
    /// <param name="comparedPropertyName">The name of the property that is being compared (case sensitive).</param>
    /// <param name="comparedValue">The value the property is being compared to.</param>
    /// <param name="disablingType">The type of disabling that should happen if the condition is NOT met. Defaulted to DisablingType.DontDraw.</param>
    public DrawIfAttribute(string comparedPropertyName, object comparedValue, DisablingType disablingType = DisablingType.DontDraw)
    {
        this.comparedPropertyName = comparedPropertyName;
        this.comparedValue = comparedValue;
        this.disablingType = disablingType;
    }
}

PropertyDrawer editor script (put inside editor folder):

using UnityEditor;
using UnityEngine;

/// <summary>
/// Based on: https://discussions.unity.com/t/650579
/// </summary>
[CustomPropertyDrawer(typeof(DrawIfAttribute))]
public class DrawIfPropertyDrawer : PropertyDrawer
{
    #region Fields

    // Reference to the attribute on the property.
    DrawIfAttribute drawIf;

    // Field that is being compared.
    SerializedProperty comparedField;

    #endregion

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        if (!ShowMe(property) && drawIf.disablingType == DrawIfAttribute.DisablingType.DontDraw)
            return 0f;
    
        // The height of the property should be defaulted to the default height.
        return base.GetPropertyHeight(property, label);
    }

    /// <summary>
    /// Errors default to showing the property.
    /// </summary>
    private bool ShowMe(SerializedProperty property)
    {
        drawIf = attribute as DrawIfAttribute;
        // Replace propertyname to the value from the parameter
        string path = property.propertyPath.Contains(".") ? System.IO.Path.ChangeExtension(property.propertyPath, drawIf.comparedPropertyName) : drawIf.comparedPropertyName;

        comparedField = property.serializedObject.FindProperty(path);

        if (comparedField == null)
        {
            Debug.LogError("Cannot find property with name: " + path);
            return true;
        }

        // get the value & compare based on types
        switch (comparedField.type)
        { // Possible extend cases to support your own type
            case "bool":
                return comparedField.boolValue.Equals(drawIf.comparedValue);
            case "Enum":
                return comparedField.enumValueIndex.Equals((int)drawIf.comparedValue);
            default:
                Debug.LogError("Error: " + comparedField.type + " is not supported of " + path);
                return true;
        }
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // If the condition is met, simply draw the field.
        if (ShowMe(property))
        {
            EditorGUI.PropertyField(position, property);
        } //...check if the disabling type is read only. If it is, draw it disabled
        else if (drawIf.disablingType == DrawIfAttribute.DisablingType.ReadOnly)
        {
            GUI.enabled = false;
            EditorGUI.PropertyField(position, property);
            GUI.enabled = true;
        }
    }

}
33 Likes

Fantastic stuff @Or-Aviram & @DreamingSpirit ! Thank you for sharing this :slight_smile:

2 Likes

Hi i took bits and pieces from here as i only need a very basic setup for what im doing. but i have noticed i can not hide an array. the values will hide but the property label is still persistant?

@Or-Aviram I am using your plugin at the moment, the conditional fields are awesome when working with non programmers on the same project.

I did not find a git page for your project so i am sending a comment here, its just a small error that makes building impossible (this might not be the case in the original versions tho) anw…

pull request: (sorta)
can u put the file “SerializedPropertyExtentions.cs” inside the plugins/drawifattribute/editor folder?
Unity gives me build errors when this is not the case.

Thanks again for a great plugin.

Thanks for everythink bro. U are sooo good mannn :smile:

Thank you for sharing this amazing post @DreamingSpirit . But there is a little issue for arrays and list fields as @RealSoftGames mentioned. Do you know how to fix this issue, thanks?

1 Like

Also, thank you @Or-Aviram but your code doesn’t work for arrays and lists too. Do you have any solution for this issue, thanks?

@sayginkarahan

“Also, thank you @Or-Aviram but your code doesn’t work for arrays and lists too. Do you have any solution for this issue, thanks?”

…you are asking a user last seen in 2017.

Unity documentation has examples of how to create property drawer for a class, those property drawer drawn classes will look the same as a field or in a list or in an array*:*

Thanks! Saved my days! :smile: