using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif namespace Lightbug.Utilities { [System.AttributeUsage(System.AttributeTargets.Field)] public class ConditionAttribute : PropertyAttribute { public enum ConditionType { IsTrue, IsFalse, IsGreaterThan, IsEqualTo, IsLessThan, IsNotNull, IsNull, } public enum VisibilityType { Hidden, NotEditable } public string[] conditionPropertyNames; public ConditionType[] conditionTypes; public float[] values; public VisibilityType visibilityType; /// /// This attribute will determine the visibility of the target property based on some other property condition. Use this attribute if the target property /// depends on some other property inside the class. /// /// Name of the property used by the condition. /// The condition type. /// The visibility action to perform if the condition is not met. /// The condition argument value. public ConditionAttribute(string conditionPropertyName, ConditionType conditionType, VisibilityType visibilityType = VisibilityType.Hidden, float conditionValue = 0f) { this.conditionPropertyNames = new string[] { conditionPropertyName }; this.conditionTypes = new ConditionType[] { conditionType }; this.visibilityType = visibilityType; this.values = new float[] { conditionValue }; } public ConditionAttribute(string[] conditionPropertyNames, ConditionType[] conditionTypes, float[] conditionValues, VisibilityType visibilityType = VisibilityType.Hidden) { this.conditionPropertyNames = conditionPropertyNames; this.conditionTypes = conditionTypes; this.visibilityType = visibilityType; this.values = conditionValues; } } #if UNITY_EDITOR [CustomPropertyDrawer(typeof(ConditionAttribute))] public class ConditionAttributeEditor : PropertyDrawer { ConditionAttribute target; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { target ??= attribute as ConditionAttribute; bool result = CheckCondition(property); if (target.visibilityType == ConditionAttribute.VisibilityType.NotEditable) { GUI.enabled = result; EditorGUI.PropertyField(position, property, label, true); GUI.enabled = true; } else { if (result) EditorGUI.PropertyField(position, property, label, true); } } bool result = false; bool CheckCondition(SerializedProperty property) { bool output = true; for (int i = 0; i < target.conditionPropertyNames.Length; i++) { output &= EvaluateCondition(property, target.conditionPropertyNames[i], target.conditionTypes[i], target.values[i]); } return output; } bool EvaluateCondition(SerializedProperty property, string conditionPropertyName, ConditionAttribute.ConditionType conditionType, float value) { SerializedProperty conditionProperty = property.serializedObject.FindProperty(conditionPropertyName); // if the "conditionProperty" is null, then the property is probably part of a plain C# serialized class. If so, then find the property root path // and look for the target condition property again. if (conditionProperty == null) { string propertyPath = property.propertyPath; int lastIndex = propertyPath.LastIndexOf('.'); if (lastIndex == -1) return true; string propertyParentPath = propertyPath.Substring(0, lastIndex); conditionProperty = property.serializedObject.FindProperty(propertyParentPath).FindPropertyRelative(conditionPropertyName); if (conditionProperty == null) return true; } result = false; SerializedPropertyType conditionPropertyType = conditionProperty.propertyType; if (conditionPropertyType == SerializedPropertyType.Boolean) { if (conditionType == ConditionAttribute.ConditionType.IsTrue) result = conditionProperty.boolValue; else if (conditionType == ConditionAttribute.ConditionType.IsFalse) result = !conditionProperty.boolValue; } else if (conditionPropertyType == SerializedPropertyType.Float) { float conditionPropertyFloatValue = conditionProperty.floatValue; float argumentFloatValue = value; switch (conditionType) { case ConditionAttribute.ConditionType.IsTrue: result = conditionPropertyFloatValue != 0f; break; case ConditionAttribute.ConditionType.IsFalse: result = conditionPropertyFloatValue == 0f; break; case ConditionAttribute.ConditionType.IsGreaterThan: result = conditionPropertyFloatValue > argumentFloatValue; break; case ConditionAttribute.ConditionType.IsEqualTo: result = conditionPropertyFloatValue == argumentFloatValue; break; case ConditionAttribute.ConditionType.IsLessThan: result = conditionPropertyFloatValue < argumentFloatValue; break; } } else if (conditionPropertyType == SerializedPropertyType.Integer || conditionPropertyType == SerializedPropertyType.Enum) { int conditionPropertyIntValue = conditionProperty.intValue; int argumentIntValue = (int)value; switch (conditionType) { case ConditionAttribute.ConditionType.IsTrue: result = conditionPropertyIntValue != 0; break; case ConditionAttribute.ConditionType.IsFalse: result = conditionPropertyIntValue == 0; break; case ConditionAttribute.ConditionType.IsGreaterThan: result = conditionPropertyIntValue > argumentIntValue; break; case ConditionAttribute.ConditionType.IsEqualTo: result = conditionPropertyIntValue == argumentIntValue; break; case ConditionAttribute.ConditionType.IsLessThan: result = conditionPropertyIntValue < argumentIntValue; break; } } else if (conditionPropertyType == SerializedPropertyType.ObjectReference) { UnityEngine.Object conditionPropertyObjectValue = conditionProperty.objectReferenceValue; switch (conditionType) { case ConditionAttribute.ConditionType.IsNull: result = conditionPropertyObjectValue == null; break; case ConditionAttribute.ConditionType.IsNotNull: result = conditionPropertyObjectValue != null; break; } } return result; } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { target ??= attribute as ConditionAttribute; return !result && target.visibilityType == ConditionAttribute.VisibilityType.Hidden ? 0f : EditorGUI.GetPropertyHeight(property); } } #endif }