Search Unity

Iterate through serializedproperty in custom property drawer

Discussion in 'Editor & General Support' started by Bullzeye231, May 18, 2020.

  1. Bullzeye231

    Bullzeye231

    Joined:
    Aug 21, 2018
    Posts:
    3
    Hello everyone,

    I have a really tough problem which I won't solve without any insider knowledge on how unity is actually handling property drawers and the such.

    I have a custom attribute on two fields within the same class.

    Code (CSharp):
    1. public class MyAttribute : PropertyAttribute
    2. {
    3.     public MyAttribute(string t) { m_declaringType = t;}
    4.     public string m_declaringType = "";
    5. }
    Code (CSharp):
    1. public class MyClass : MonoBehaviour
    2. {
    3.             [MyAttribute(typeof(DropdownManager2).Name)]
    4.             [SerializeField] protected bool m_lockOnSelection     = false;          
    5.             [MyAttribute]
    6.             [SerializeField] private TMP_Text m_outputText        = null;
    7. }
    The property drawer responsible for the serialized property thats tagged with the attribute does the following:

    Code (CSharp):
    1.     [CustomPropertyDrawer(typeof(MyAttribute))]
    2.     public class MyAttributeDrawer : PropertyDrawer
    3.     {
    4.         /*=====================*\
    5.         |*   Unity Functions   *|
    6.         \*=====================*/
    7.  
    8.         public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label)
    9.         {
    10.             // Manager tasks
    11.             // -------------
    12.             if ((attribute as MyAttribute).m_declaringType != "")
    13.             {
    14.                 // Update searialized Object
    15.                 // -------------------------
    16.                 prop.serializedObject.Update();
    17.  
    18.                 // Define locals
    19.                 // -------------
    20.                 SerializedProperty iterator = prop.serializedObject.GetIterator();
    21.  
    22.                 Rect formerRect = position;
    23.  
    24.                 // Iterate over properties
    25.                 // -----------------------
    26.                 do
    27.                 {
    28.                     // Fetch iterator field
    29.                     // --------------------
    30.                     iteratorField = prop.serializedObject.targetObject.GetType()
    31.                                 .GetField(iterator.name, BindingFlags.NonPublic | BindingFlags.Instance);
    32.  
    33.                     // Skip (Field not found)
    34.                     // ----------------------
    35.                     if (iteratorField == null) continue;
    36.                
    37.                     // Skip (Parent or child classes)
    38.                     // ------------------------------
    39.                     if (iteratorField.DeclaringType.Name != (attribute as MyAttribute).m_declaringType) continue;
    40.                
    41.                     // Skip (Not an MyAttribute Field)
    42.                     // ------------------------------------
    43.                     if (!iteratorField.IsDefined(typeof(MyAttribute), false)) continue;
    44.  
    45.                     // Draw Field
    46.                     // --------------
    47.                     EditorGUI.PropertyField(formerRect, iterator);
    48.                     formerRect.y += 18;
    49.                 }
    50.                 while (iterator.Next(true));
    51.  
    52.                 // Save changes
    53.                 // ------------
    54.                 prop.serializedObject.ApplyModifiedProperties();
    55.             }
    56.         }
    The strange thing here is that the first MyAttribute is drawn whereas the second one is missing.
    I nailed it down to the following scenario, however I'm not sure if that really is the case:

    Since the propertyDrawer is called for every field with the specified Attribute, the OnGUI function is called twice. Once where it passes the if condition "if ((attribute as MyAttribute).m_declaringType != "")" and once without. During the first call the Iterator iterates through the serializedObject and draws all property fields marked with MyAttribute. Since the property drawer is called the second time without doing anything it might override/overdraw the initial draw of the TMP_Text outputText, to not display anything.

    To further understand this problem I need some knowledge about how the drawing actually works and when ApplyModifiedProperties is writing the serializedObjectData back to its connected object.

    By the way, the reason I'm doing this is that List<T> is not supported by the PropertyDrawer and I don't want to write tons of CustomEditors when I'm always using the same attributes for the same purpose.

    Any help would be appreciated

    Greetings Bullzeye231