Search Unity

[Sort of release] Validate values with attributes while keeping additional propertydrawers

Discussion in 'Immediate Mode GUI (IMGUI)' started by HOEKKII-DEV, Jul 1, 2017.

  1. HOEKKII-DEV

    HOEKKII-DEV

    Joined:
    Dec 26, 2014
    Posts:
    31
    Here is some code I just made and maybe usefull for anyone else.
    Its purpose is to validate fields in the inspector when they get changed.
    You could do this with a PropertyAttribute + PropertyDrawer, but it doesn't work when you also want an additional PropertyDrawer.

    The spoiler with the tag "Code" contains the required code, and the spoiler beneeth "Example" contains an example. As shown in the example, you use RootBehaviour instead of MonoBehaviour. (For those who are nitpicky; yeah I know the exaple doesn't add anything usefull, but it shows where it is for)

    Code (CSharp):
    1. using System.Reflection;
    2. using UnityEngine;
    3.  
    4. public abstract class RootBehaviour : MonoBehaviour
    5. {
    6.     #if UNITY_EDITOR
    7.     protected virtual void OnValidate()
    8.     {
    9.         // Predefine flags
    10.         const BindingFlags flags =
    11.               BindingFlags.Public
    12.             | BindingFlags.NonPublic
    13.             | BindingFlags.Instance;
    14.  
    15.         // Get fields
    16.         var type = GetType();
    17.         var fields = type.GetFields(flags);
    18.         foreach (var field in fields)
    19.         {
    20.             // Get field value
    21.             var attributes = field.GetCustomAttributes(typeof(ValidateAttribute), true);
    22.  
    23.             // Check if has any attributes to safe some calculations
    24.             if (attributes.Length < 1)
    25.                 continue;
    26.  
    27.             // Get the field its value
    28.             var value = field.GetValue(this);
    29.  
    30.             // Validate every field
    31.             foreach (var attribute in attributes)
    32.             {
    33.                 // Convert to validate
    34.                 var validateAttribute = attribute as ValidateAttribute;
    35.                 if (validateAttribute == null)
    36.                     continue;
    37.  
    38.                 // Perform action
    39.                 validateAttribute.Validate(ref value, field.FieldType);
    40.             }
    41.  
    42.             // Apply
    43.             field.SetValue(this, value);
    44.         }
    45.     }
    46.     #else
    47.     protected virtual void OnValidate() { }
    48.     #endif
    49. }
    Code (CSharp):
    1. using System;
    2. using System.Diagnostics;
    3.  
    4. public abstract class ValidateAttribute : Attribute
    5. {
    6.     [Conditional("UNITY_EDITOR")]
    7.     public abstract void Validate(ref object value, Type type);
    8. }

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. /// <summary>
    5. ///     Makes sure the value is not set to a lower value than the indicated value when altering in the inspector
    6. /// </summary>
    7. public class MinAttribute : ValidateAttribute
    8. {
    9.     public readonly float Min;
    10.  
    11.     /// <summary>
    12.     ///     Constructor
    13.     /// </summary>
    14.     /// <param name="min">Min value of the variable</param>
    15.     public MinAttribute(float min = 0.0f)
    16.     {
    17.         Min = min;
    18.     }
    19.  
    20.     /// <summary>
    21.     ///     Constructor
    22.     /// </summary>
    23.     /// <param name="min">Min value of the variable</param>
    24.     public MinAttribute(int min)
    25.     {
    26.         Min = min;
    27.     }
    28.  
    29.     /// <inheritdoc />
    30.     public override void Validate(ref object value, Type type)
    31.     {
    32.         if (type == typeof(int))
    33.         {
    34.             var number = (int)value;
    35.             if (number < Min)
    36.                 number = (int)(Min + 0.5f);
    37.             value = number;
    38.         }
    39.         else if (type == typeof(float))
    40.         {
    41.             var number = (float)value;
    42.             if (number < Min)
    43.                 number = Min;
    44.             value = number;
    45.         }
    46.         else
    47.             Debug.LogError("<color=blue>[MinAttribute]</color> " + type + " is not supported with this attribute");
    48.     }
    49. }
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class ___Test : RootBehaviour
    4. {
    5.     [SerializeField, Min, Range(-1, 1)]
    6.     float _myPrivateVar;
    7. }
    8.  
     
    Dustin_00 likes this.
  2. HOEKKII-DEV

    HOEKKII-DEV

    Joined:
    Dec 26, 2014
    Posts:
    31
    An update to this script; It now supports lists and serializable classes.

    Code (CSharp):
    1. public class Example : RootBehaviour
    2. {
    3.     [SerializeField] List<MyClass> _myItems;
    4.     [SerializeField, Min(5)] int _myInt;
    5.     [SerializeField, Min(-10.0f)] List<float> _myItems2;
    6.  
    7.     [Serializable]
    8.     public class MyClass
    9.     {
    10.         [Min] public float K;
    11.     }
    12. }
     

    Attached Files:

    Dustin_00 likes this.