Skip to content

Instantly share code, notes, and snippets.

@deebrol
Last active February 25, 2024 23:53
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deebrol/02f61b7611fd4eca923776077b92dfc2 to your computer and use it in GitHub Desktop.
Save deebrol/02f61b7611fd4eca923776077b92dfc2 to your computer and use it in GitHub Desktop.
Property Drawer for Unity used to show or hide the Field depending on certain conditions
// DONT PUT IN EDITOR FOLDER
using System;
using UnityEngine;
/// <summary>
/// Attribute used to show or hide the Field depending on certain conditions
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class ShowWhenAttribute : PropertyAttribute {
public readonly string conditionFieldName;
public readonly object comparationValue;
public readonly object[] comparationValueArray;
/// <summary>
/// Attribute used to show or hide the Field depending on certain conditions
/// </summary>
/// <param name="conditionFieldName">Name of the bool condition Field</param>
public ShowWhenAttribute(string conditionFieldName)
{
this.conditionFieldName = conditionFieldName;
}
/// <summary>
/// Attribute used to show or hide the Field depending on certain conditions
/// </summary>
/// <param name="conditionFieldName">Name of the Field to compare (bool, enum, int or float)</param>
/// <param name="comparationValue">Value to compare</param>
public ShowWhenAttribute(string conditionFieldName, object comparationValue = null)
{
this.conditionFieldName = conditionFieldName;
this.comparationValue = comparationValue;
}
/// <summary>
/// Attribute used to show or hide the Field depending on certain conditions
/// </summary>
/// <param name="conditionFieldName">Name of the Field to compare (bool, enum, int or float)</param>
/// <param name="comparationValueArray">Array of values to compare (only for enums)</param>
public ShowWhenAttribute(string conditionFieldName, object[] comparationValueArray = null)
{
this.conditionFieldName = conditionFieldName;
this.comparationValueArray = comparationValueArray;
}
}
// PUT IN EDITOR FOLDER
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer(typeof(ShowWhenAttribute))]
public class ShowWhenDrawer : PropertyDrawer
{
private bool showField = true;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
ShowWhenAttribute attribute = (ShowWhenAttribute) this.attribute;
SerializedProperty conditionField = property.serializedObject.FindProperty(attribute.conditionFieldName);
// We check that exist a Field with the parameter name
if (conditionField == null)
{
ShowError(position, label, "Error getting the condition Field. Check the name.");
return;
}
switch (conditionField.propertyType)
{
case SerializedPropertyType.Boolean:
try
{
bool comparationValue = attribute.comparationValue == null || (bool)attribute.comparationValue;
showField = conditionField.boolValue == comparationValue;
}
catch
{
ShowError(position, label, "Invalid comparation Value Type");
return;
}
break;
case SerializedPropertyType.Enum:
object paramEnum = attribute.comparationValue;
object[] paramEnumArray = attribute.comparationValueArray;
if ( paramEnum == null && paramEnumArray == null)
{
ShowError(position, label, "The comparation enum value is null");
return;
}
else if (IsEnum(paramEnum))
{
if (!CheckSameEnumType(new[] {paramEnum.GetType()}, property.serializedObject.targetObject.GetType(), conditionField.propertyPath))
{
ShowError(position, label, "Enum Types doesn't match");
return;
}
else
{
string enumValue = Enum.GetValues(paramEnum.GetType()).GetValue(conditionField.enumValueIndex).ToString();
if (paramEnum.ToString() != enumValue)
showField = false;
else
showField = true;
}
}
else if (IsEnum(paramEnumArray))
{
if (!CheckSameEnumType(paramEnumArray.Select(x => x.GetType()), property.serializedObject.targetObject.GetType(), conditionField.propertyPath))
{
ShowError(position, label, "Enum Types doesn't match");
return;
}
else
{
string enumValue = Enum.GetValues(paramEnumArray[0].GetType()).GetValue(conditionField.enumValueIndex).ToString();
if (paramEnumArray.All(x => x.ToString() != enumValue))
showField = false;
else
showField = true;
}
}
else
{
ShowError(position, label, "The comparation enum value is not an enum");
return;
}
break;
case SerializedPropertyType.Integer:
case SerializedPropertyType.Float:
string stringValue;
bool error = false;
float conditionValue = 0;
if (conditionField.propertyType == SerializedPropertyType.Integer)
conditionValue = conditionField.intValue;
else if (conditionField.propertyType == SerializedPropertyType.Float)
conditionValue = conditionField.floatValue;
try
{
stringValue = (string)attribute.comparationValue;
}
catch
{
ShowError(position, label, "Invalid comparation Value Type");
return;
}
if (stringValue.StartsWith("=="))
{
float? value = GetValue(stringValue, "==");
if (value == null)
error = true;
else
showField = conditionValue == value;
}
else if (stringValue.StartsWith("!="))
{
float? value = GetValue(stringValue, "!=");
if (value == null)
error = true;
else
showField = conditionValue != value;
}
else if (stringValue.StartsWith("<="))
{
float? value = GetValue(stringValue, "<=");
if (value == null)
error = true;
else
showField = conditionValue <= value;
}
else if (stringValue.StartsWith(">="))
{
float? value = GetValue(stringValue, ">=");
if (value == null)
error = true;
else
showField = conditionValue >= value;
}
else if (stringValue.StartsWith("<"))
{
float? value = GetValue(stringValue, "<");
if (value == null)
error = true;
else
showField = conditionValue < value;
}
else if (stringValue.StartsWith(">"))
{
float? value = GetValue(stringValue, ">");
if (value == null)
error = true;
else
showField = conditionValue > value;
}
if (error)
{
ShowError(position, label, "Invalid comparation instruction for Int or float value");
return;
}
break;
default:
ShowError(position, label, "This type has not supported.");
return;
}
if (showField)
EditorGUI.PropertyField(position, property, true);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
if (showField)
return EditorGUI.GetPropertyHeight(property);
else
return -EditorGUIUtility.standardVerticalSpacing;
}
/// <summary>
/// Return if the object is enum and not null
/// </summary>
private static bool IsEnum(object obj)
{
return obj != null && obj.GetType().IsEnum;
}
/// <summary>
/// Return if all the objects are enums and not null
/// </summary>
private static bool IsEnum(object[] obj)
{
return obj != null && obj.All(o => o.GetType().IsEnum);
}
/// <summary>
/// Check if the field with name "fieldName" has the same class as the "checkTypes" classes through reflection
/// </summary>
private static bool CheckSameEnumType(IEnumerable<Type> checkTypes, Type classType, string fieldName)
{
FieldInfo memberInfo;
string[] fields = fieldName.Split('.');
if (fields.Length > 1)
{
memberInfo = classType.GetField(fields[0]);
for (int i = 1; i < fields.Length; i++)
{
memberInfo = memberInfo.FieldType.GetField(fields[i]);
}
}
else
memberInfo = classType.GetField(fieldName);
if (memberInfo != null)
return checkTypes.All(x => x == memberInfo.FieldType);
return false;
}
private void ShowError(Rect position, GUIContent label, string errorText)
{
EditorGUI.LabelField(position, label, new GUIContent(errorText));
showField = true;
}
/// <summary>
/// Return the float value in the content string removing the remove string
/// </summary>
private static float? GetValue(string content, string remove)
{
string removed = content.Replace(remove, "");
try
{
return float.Parse(removed);
}
catch
{
return null;
}
}
}
using System;
using UnityEngine;
public class ShowWhenExample : MonoBehaviour
{
#region Bool Region
[Header("Check with BOOL")]
public bool show;
[ShowWhen("show")]
public float numberWhenTrue;
[ShowWhen("show", false)]
public Vector3 vector3WhenFalse;
[ShowWhen("show")]
public float[] arrayWhenTrue = {1, 2, 3};
[Serializable]
public class ArrayClass
{
public Color[] colorArray = {Color.red, Color.blue, Color.green};
}
[ShowWhen("show")]
public ArrayClass workaroundForArrayWhenTrue;
[Serializable]
public class CustomClass
{
public float floatValue = 99;
public string stringValue = "string";
}
[ShowWhen("show", true)]
public CustomClass customClass;
[ShowWhen("show", "error")]
public string stringErrorComparationValueType;
#endregion
#region Enum Region
public enum EaseType
{
Linear,
OutQuad,
InOutQuint
}
public enum OtherEnum
{
Enum1
}
[Space(20), Header("Check with ENUM")]
public EaseType easeType;
[ShowWhen("easeType", EaseType.Linear)]
public string stringWhenLinear = "Linear";
[ShowWhen("easeType", new object[]{EaseType.Linear, EaseType.OutQuad})]
public string stringWhenLinearAndOutQuad = "LinearAndOutQuad";
[ShowWhen("easeType", EaseType.InOutQuint)]
public string stringWhenInOutQuint = "InOutQuint";
[ShowWhen("easeType")]
public string stringErrorNeedParam;
[ShowWhen("easeType",5)]
public string stringErrorNotEnum;
public OtherEnum otherEnum;
[ShowWhen("otherEnum", new object[]{OtherEnum.Enum1, EaseType.Linear})]
public string stringErrorWrongEnumType;
#endregion
#region Int Region
[Space(20), Header("Check with INT")]
public int intValue;
[ShowWhen("intValue", ">5")]
public string stringWhenGreaterThan5 = "Greater Than 5";
[ShowWhen("intValue", ">3+5")]
public string stringErrorNotKnownOperation;
#endregion
#region Float Region
[Space(20), Header("Check with FLOAT")]
public float floatValue;
[ShowWhen("floatValue", "!=2")]
public GameObject showWhenOtherThan2;
[ShowWhen("floatValueError", ">5")]
public string stringErrorParameterNotKnown;
[ShowWhen("stringErrorParameterNotKnown", ">+5")]
public string stringErrorTypeNotSupported;
#endregion
}
@Autumn1992
Copy link

Unity2017.3.1p4 can not fully hide List or Array field as wish(maybe unity new version changed the drawer). do you have a solution.

@longtran2904
Copy link

longtran2904 commented Oct 25, 2020

Do this attribute work in a non-monobehaviour and serializable class? I had a non-monobehaviour and serializable class and I have an instance of that class in a monobehaviour script but for some reason, it just keeps saying "Error getting the condition Field. Check the name." (I have an enum StatusType and a StatusType type). Also, the script doesn't update in the inspector (e.g When I change some value it didn't get updated).

public enum StatusType { Burn, Bleed, Poison, Freeze, Slow, Blind, Injured }

[System.Serializable]
public class State
{
    public StatusType type;
    public float duration;
    [ShowWhen("duration", ">0")] // This is for testing. It didn't updated if I change the duration value
    public float percent;
    [ShowWhen("type", new object[] { StatusType.Burn, StatusType.Bleed, StatusType.Poison })]
    public int damage;
    [ShowWhen("type", new object[] { StatusType.Burn, StatusType.Bleed, StatusType.Poison })]
    public float timeBtwHits;
}

public class Item : MonoBehaviour
{
  public float duration; // If I change this the percent change
  public State state;
}

@deebrol
Copy link
Author

deebrol commented Oct 29, 2020

Hello @longtran2904 if you want to reference a field inside a serialized class you have to write all the path (with a dot between fields) in the drawer like this:

[ShowWhen("state.duration", ">0")] // This is for testing. It didn't updated if I change the duration value
public float percent;
[ShowWhen("state.type", new object[] { StatusType.Burn, StatusType.Bleed, StatusType.Poison })] 
public int damage;
[ShowWhen("state.type", new object[] { StatusType.Burn, StatusType.Bleed, StatusType.Poison })]
public float timeBtwHits;

If not you are referencing the main class fields like in you example.

Also I've updated the drawer to work properly with enums inside a serialized class. Hope this help to you.

@longtran2904
Copy link

Thank you! But I had posted the question in the unity answer and got the answer http://answers.unity.com/answers/1783260/view.html. I don't like to write the full path because sometimes I have to have an instance of that class in multiple scripts.

@longtran2904
Copy link

This decorator drawer doesn't work properly with any property drawer. Can I both have a decorator and property drawer on a field?

@longtran2904
Copy link

When you have a custom class with the attribute in it and have a field of its type in another class then it won't show up correctly in the inspector. I'm currently using Unity 2020.3.12f1

[Serializable]
class A
{
     public bool condition;
     [ShowWhen("condition")] public int data;
}

public class B : MonoBehaviour
{
     public A a; // The inspector only shows the value of "a.data" but won't show the name "data" of the field.
}

@longtran2904
Copy link

longtran2904 commented Jul 13, 2021

I've just realized that it's not working in Unity 2020.3.12f1. The field's value is still shown in the inspector but the field's name isn't. It's not working for all fields, not just the example above.

@Shubham-EV
Copy link

I've just realized that it's not working in Unity 2020.3.12f1. The field's value is still shown in the inspector but the field's name isn't. It's not working for all fields, not just the example above.

yes field name isn't showing now plz fix it

@Coopalooper
Copy link

I've just realized that it's not working in Unity 2020.3.12f1. The field's value is still shown in the inspector but the field's name isn't. It's not working for all fields, not just the example above.

Just change line 170 in ShowWhenDrawer.cs to be:
EditorGUI.PropertyField(position, property, label, true);

This should display the label in the inspector. 😎 👍
image

@mstruzyna
Copy link

mstruzyna commented Mar 17, 2022

I am using Unity 2021.1.11f1
Fixes I have made or integrated:

  • @Coopalooper fix
  • @longtran2904 fix with fields inside serialized classes - one more file is needed (attached)
  • I made my own fix to the drawing since the code here calculates showField in OnGui and uses this in GetPropertyHeight function but the GetPropertyHeight function is actually called before OnGui so it uses height from the previous calculated field which destroyed my arrays :)
  • I made my own fix to the CheckSameEnumType function - now it works with Arrays

`[CustomPropertyDrawer(typeof(ShowWhenAttribute))]
public class ShowWhenDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
bool showField = ShouldShowField(property, out bool hasError, out string errorMessage);

    if (hasError)
    {
        ShowError(position, label, errorMessage);
        return;
    }

    if (showField)
        EditorGUI.PropertyField(position, property, label, true);
}

bool ShouldShowField(SerializedProperty property, out bool hasError, out string errorMessage)
{
    bool showField = true;

    ShowWhenAttribute attribute = (ShowWhenAttribute)this.attribute;
    hasError = false;
    errorMessage = "";

    //SerializedProperty conditionField = property.serializedObject.FindProperty(attribute.conditionFieldName);

    SerializedProperty conditionField = property.FindSiblingProperty(attribute.conditionFieldName);

    // We check that exist a Field with the parameter name
    if (conditionField == null)
    {
        hasError = true;
        errorMessage = "Error getting the condition Field. Check the name.";
        return true;// Errors should be displayed
    }

    switch (conditionField.propertyType)
    {
        case SerializedPropertyType.Boolean:
            try
            {
                bool comparationValue = attribute.comparationValue == null || (bool)attribute.comparationValue;
                showField = conditionField.boolValue == comparationValue;
            }
            catch
            {
                hasError = true;
                errorMessage = "Invalid comparation Value Type";
                return true;// Errors should be displayed
            }
            break;
        case SerializedPropertyType.Enum:
            object paramEnum = attribute.comparationValue;
            object[] paramEnumArray = attribute.comparationValueArray;

            if (paramEnum == null && paramEnumArray == null)
            {
                hasError = true;
                errorMessage = "The comparation enum value is null";
                return true;// Errors should be displayed
            }
            else if (IsEnum(paramEnum))
            {
                if (!CheckSameEnumType(new[] { paramEnum.GetType() }, property.serializedObject.targetObject.GetType(), conditionField.propertyPath))
                {
                    hasError = true;
                    errorMessage = "Enum Types doesn't match";
                    return true;// Errors should be displayed
                }
                else
                {
                    string enumValue = Enum.GetValues(paramEnum.GetType()).GetValue(conditionField.enumValueIndex).ToString();
                    if (paramEnum.ToString() != enumValue)
                        showField = false;
                    else
                        showField = true;
                }
            }
            else if (IsEnum(paramEnumArray))
            {
                if (!CheckSameEnumType(paramEnumArray.Select(x => x.GetType()), property.serializedObject.targetObject.GetType(), conditionField.propertyPath))
                {
                    hasError = true;
                    errorMessage = "Enum Types doesn't match";
                    return true;// Errors should be displayed
                }
                else
                {
                    string enumValue = Enum.GetValues(paramEnumArray[0].GetType()).GetValue(conditionField.enumValueIndex).ToString();
                    if (paramEnumArray.All(x => x.ToString() != enumValue))
                        showField = false;
                    else
                        showField = true;
                }
            }
            else
            {
                hasError = true;
                errorMessage = "The comparation enum value is not an enum";
                return true;// Errors should be displayed
            }
            break;
        case SerializedPropertyType.Integer:
        case SerializedPropertyType.Float:
            string stringValue;
            bool error = false;

            float conditionValue = 0;
            if (conditionField.propertyType == SerializedPropertyType.Integer)
                conditionValue = conditionField.intValue;
            else if (conditionField.propertyType == SerializedPropertyType.Float)
                conditionValue = conditionField.floatValue;

            try
            {
                stringValue = (string)attribute.comparationValue;
            }
            catch
            {
                hasError = true;
                errorMessage = "Invalid comparation Value Type";
                return true;// Errors should be displayed
            }

            if (stringValue.StartsWith("=="))
            {
                float? value = GetValue(stringValue, "==");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue == value;
            }
            else if (stringValue.StartsWith("!="))
            {
                float? value = GetValue(stringValue, "!=");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue != value;
            }
            else if (stringValue.StartsWith("<="))
            {
                float? value = GetValue(stringValue, "<=");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue <= value;
            }
            else if (stringValue.StartsWith(">="))
            {
                float? value = GetValue(stringValue, ">=");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue >= value;
            }
            else if (stringValue.StartsWith("<"))
            {
                float? value = GetValue(stringValue, "<");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue < value;
            }
            else if (stringValue.StartsWith(">"))
            {
                float? value = GetValue(stringValue, ">");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue > value;
            }

            if (error)
            {
                hasError = true;
                errorMessage = "Invalid comparation instruction for Int or float value";
                return true;// Errors should be displayed
            }
            break;
        default:
            hasError = true;
            errorMessage = "This type has not supported.";
            return true;// Errors should be displayed
    }

    return showField;
}

public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
    if (ShouldShowField(property, out bool error, out string errorMessage))
        return EditorGUI.GetPropertyHeight(property);

    return 0.0f;
    //else
    //    return -EditorGUIUtility.standardVerticalSpacing;
}

/// <summary>
/// Return if the object is enum and not null
/// </summary>
private static bool IsEnum(object obj)
{
    return obj != null && obj.GetType().IsEnum;
}

/// <summary>
/// Return if all the objects are enums and not null
/// </summary>
private static bool IsEnum(object[] obj)
{
    return obj != null && obj.All(o => o.GetType().IsEnum);
}

/// <summary>
/// Check if the field with name "fieldName" has the same class as the "checkTypes" classes through reflection
/// </summary>
private static bool CheckSameEnumType(IEnumerable<Type> checkTypes, Type classType, string fieldName)
{
    Type currentFieldType;
    string[] fields = fieldName.Split('.');
    if (fields.Length > 1)
    {
        currentFieldType = classType.GetField(fields[0]).FieldType;
        for (int i = 1; i < fields.Length; i++)
        {
            if (currentFieldType.IsArray)
            {
                currentFieldType = currentFieldType.GetElementType();// GetFields()[fieldIdx];

                i += 1;// The fieldNames for array will containt Array.data[0] so we need to skip two
            }
            else
                currentFieldType = currentFieldType.GetField(fields[i]).FieldType;
        }
    }
    else
        currentFieldType = classType.GetField(fieldName).FieldType;

    if (currentFieldType != null)
        return checkTypes.All(x => x == currentFieldType);

    return false;
}

private void ShowError(Rect position, GUIContent label, string errorText)
{
    EditorGUI.LabelField(position, label, new GUIContent(errorText));
}

/// <summary>
/// Return the float value in the content string removing the remove string
/// </summary>
private static float? GetValue(string content, string remove)
{
    string removed = content.Replace(remove, "");
    try
    {
        return float.Parse(removed);
    }
    catch
    {
        return null;
    }
}

}`

And the new file

public static class SerializedPropertyExt { public static SerializedProperty GetParent(this SerializedProperty aProperty) { var path = aProperty.propertyPath; int i = path.LastIndexOf('.'); if (i < 0) return null; return aProperty.serializedObject.FindProperty(path.Substring(0, i)); } public static SerializedProperty FindSiblingProperty(this SerializedProperty aProperty, string aPath) { var parent = aProperty.GetParent(); if (parent == null) return aProperty.serializedObject.FindProperty(aPath); return parent.FindPropertyRelative(aPath); } }

@longtran2904
Copy link

I'm not familiar with writing property drawer in Unity, so if someone can point me in the right direction then very much appreciated:

  1. How can I parse a "not enum" expression? Currently, the only way to do it is to have an array of every other enum value except the one.
  2. How can I parse multiple bools? I couldn't have multiple attributes for the same field and the array attribute is only for an array of enums.
  3. This is a small one and I don't need it but occasionally I have the same condition for multiple fields so I wonder if there is any way for me to mark the first field and then the drawer just continue to draw until the end or hit another attribute.

@Uzuzuru
Copy link

Uzuzuru commented Aug 18, 2023

I dont know what I m doing wrong but it doesn't work.
public bool withDuration; public float duration; [Range(1, 10)] public int stackLimit; [ShowWhen("withDuration")] public bool runEveryTick; public bool revertOnEnd;

It says "error getting the condition field". Its a serializable class( non mono ) .

@dashadowofcat
Copy link

dashadowofcat commented Aug 21, 2023

I am using Unity 2021.1.11f1 Fixes I have made or integrated:

  • @Coopalooper fix
  • @longtran2904 fix with fields inside serialized classes - one more file is needed (attached)
  • I made my own fix to the drawing since the code here calculates showField in OnGui and uses this in GetPropertyHeight function but the GetPropertyHeight function is actually called before OnGui so it uses height from the previous calculated field which destroyed my arrays :)
  • I made my own fix to the CheckSameEnumType function - now it works with Arrays

`[CustomPropertyDrawer(typeof(ShowWhenAttribute))] public class ShowWhenDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { bool showField = ShouldShowField(property, out bool hasError, out string errorMessage);

    if (hasError)
    {
        ShowError(position, label, errorMessage);
        return;
    }

    if (showField)
        EditorGUI.PropertyField(position, property, label, true);
}

bool ShouldShowField(SerializedProperty property, out bool hasError, out string errorMessage)
{
    bool showField = true;

    ShowWhenAttribute attribute = (ShowWhenAttribute)this.attribute;
    hasError = false;
    errorMessage = "";

    //SerializedProperty conditionField = property.serializedObject.FindProperty(attribute.conditionFieldName);

    SerializedProperty conditionField = property.FindSiblingProperty(attribute.conditionFieldName);

    // We check that exist a Field with the parameter name
    if (conditionField == null)
    {
        hasError = true;
        errorMessage = "Error getting the condition Field. Check the name.";
        return true;// Errors should be displayed
    }

    switch (conditionField.propertyType)
    {
        case SerializedPropertyType.Boolean:
            try
            {
                bool comparationValue = attribute.comparationValue == null || (bool)attribute.comparationValue;
                showField = conditionField.boolValue == comparationValue;
            }
            catch
            {
                hasError = true;
                errorMessage = "Invalid comparation Value Type";
                return true;// Errors should be displayed
            }
            break;
        case SerializedPropertyType.Enum:
            object paramEnum = attribute.comparationValue;
            object[] paramEnumArray = attribute.comparationValueArray;

            if (paramEnum == null && paramEnumArray == null)
            {
                hasError = true;
                errorMessage = "The comparation enum value is null";
                return true;// Errors should be displayed
            }
            else if (IsEnum(paramEnum))
            {
                if (!CheckSameEnumType(new[] { paramEnum.GetType() }, property.serializedObject.targetObject.GetType(), conditionField.propertyPath))
                {
                    hasError = true;
                    errorMessage = "Enum Types doesn't match";
                    return true;// Errors should be displayed
                }
                else
                {
                    string enumValue = Enum.GetValues(paramEnum.GetType()).GetValue(conditionField.enumValueIndex).ToString();
                    if (paramEnum.ToString() != enumValue)
                        showField = false;
                    else
                        showField = true;
                }
            }
            else if (IsEnum(paramEnumArray))
            {
                if (!CheckSameEnumType(paramEnumArray.Select(x => x.GetType()), property.serializedObject.targetObject.GetType(), conditionField.propertyPath))
                {
                    hasError = true;
                    errorMessage = "Enum Types doesn't match";
                    return true;// Errors should be displayed
                }
                else
                {
                    string enumValue = Enum.GetValues(paramEnumArray[0].GetType()).GetValue(conditionField.enumValueIndex).ToString();
                    if (paramEnumArray.All(x => x.ToString() != enumValue))
                        showField = false;
                    else
                        showField = true;
                }
            }
            else
            {
                hasError = true;
                errorMessage = "The comparation enum value is not an enum";
                return true;// Errors should be displayed
            }
            break;
        case SerializedPropertyType.Integer:
        case SerializedPropertyType.Float:
            string stringValue;
            bool error = false;

            float conditionValue = 0;
            if (conditionField.propertyType == SerializedPropertyType.Integer)
                conditionValue = conditionField.intValue;
            else if (conditionField.propertyType == SerializedPropertyType.Float)
                conditionValue = conditionField.floatValue;

            try
            {
                stringValue = (string)attribute.comparationValue;
            }
            catch
            {
                hasError = true;
                errorMessage = "Invalid comparation Value Type";
                return true;// Errors should be displayed
            }

            if (stringValue.StartsWith("=="))
            {
                float? value = GetValue(stringValue, "==");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue == value;
            }
            else if (stringValue.StartsWith("!="))
            {
                float? value = GetValue(stringValue, "!=");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue != value;
            }
            else if (stringValue.StartsWith("<="))
            {
                float? value = GetValue(stringValue, "<=");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue <= value;
            }
            else if (stringValue.StartsWith(">="))
            {
                float? value = GetValue(stringValue, ">=");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue >= value;
            }
            else if (stringValue.StartsWith("<"))
            {
                float? value = GetValue(stringValue, "<");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue < value;
            }
            else if (stringValue.StartsWith(">"))
            {
                float? value = GetValue(stringValue, ">");
                if (value == null)
                    error = true;
                else
                    showField = conditionValue > value;
            }

            if (error)
            {
                hasError = true;
                errorMessage = "Invalid comparation instruction for Int or float value";
                return true;// Errors should be displayed
            }
            break;
        default:
            hasError = true;
            errorMessage = "This type has not supported.";
            return true;// Errors should be displayed
    }

    return showField;
}

public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
    if (ShouldShowField(property, out bool error, out string errorMessage))
        return EditorGUI.GetPropertyHeight(property);

    return 0.0f;
    //else
    //    return -EditorGUIUtility.standardVerticalSpacing;
}

/// <summary>
/// Return if the object is enum and not null
/// </summary>
private static bool IsEnum(object obj)
{
    return obj != null && obj.GetType().IsEnum;
}

/// <summary>
/// Return if all the objects are enums and not null
/// </summary>
private static bool IsEnum(object[] obj)
{
    return obj != null && obj.All(o => o.GetType().IsEnum);
}

/// <summary>
/// Check if the field with name "fieldName" has the same class as the "checkTypes" classes through reflection
/// </summary>
private static bool CheckSameEnumType(IEnumerable<Type> checkTypes, Type classType, string fieldName)
{
    Type currentFieldType;
    string[] fields = fieldName.Split('.');
    if (fields.Length > 1)
    {
        currentFieldType = classType.GetField(fields[0]).FieldType;
        for (int i = 1; i < fields.Length; i++)
        {
            if (currentFieldType.IsArray)
            {
                currentFieldType = currentFieldType.GetElementType();// GetFields()[fieldIdx];

                i += 1;// The fieldNames for array will containt Array.data[0] so we need to skip two
            }
            else
                currentFieldType = currentFieldType.GetField(fields[i]).FieldType;
        }
    }
    else
        currentFieldType = classType.GetField(fieldName).FieldType;

    if (currentFieldType != null)
        return checkTypes.All(x => x == currentFieldType);

    return false;
}

private void ShowError(Rect position, GUIContent label, string errorText)
{
    EditorGUI.LabelField(position, label, new GUIContent(errorText));
}

/// <summary>
/// Return the float value in the content string removing the remove string
/// </summary>
private static float? GetValue(string content, string remove)
{
    string removed = content.Replace(remove, "");
    try
    {
        return float.Parse(removed);
    }
    catch
    {
        return null;
    }
}

}`

And the new file

public static class SerializedPropertyExt { public static SerializedProperty GetParent(this SerializedProperty aProperty) { var path = aProperty.propertyPath; int i = path.LastIndexOf('.'); if (i < 0) return null; return aProperty.serializedObject.FindProperty(path.Substring(0, i)); } public static SerializedProperty FindSiblingProperty(this SerializedProperty aProperty, string aPath) { var parent = aProperty.GetParent(); if (parent == null) return aProperty.serializedObject.FindProperty(aPath); return parent.FindPropertyRelative(aPath); } }

i used this and got the error "Error CS1061 'SerializedProperty' does not contain a definition for 'FindSiblingProperty' and no accessible extension method 'FindSiblingProperty' accepting a first argument of type 'SerializedProperty' could be found (are you missing a using directive or an assembly reference?)"

@mstruzyna
Copy link

There is the "And the new file" section in my reply. You need to create another file and paste this code. The FindSiblingProperty extension is defined there

@dashadowofcat
Copy link

There is the "And the new file" section in my reply. You need to create another file and paste this code. The FindSiblingProperty extension is defined there

thank you! it works now

@dashadowofcat
Copy link

also for some reason it dosnt work for arrays

@dmauro
Copy link

dmauro commented Feb 25, 2024

Made a change to this so that it works in properties that aren't at the root level, solution for which was found here: https://www.brechtos.com/hiding-or-disabling-inspector-properties-using-propertydrawers-within-unity-5/

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        ShowWhenAttribute attribute = (ShowWhenAttribute) this.attribute;
        var propPath = property.propertyPath;
        var conditionalPath = propPath.Replace(property.name, attribute.conditionFieldName);
        SerializedProperty conditionField = property.serializedObject.FindProperty(conditionalPath);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment