Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug LTS 2022.3.2f1 - PropertyField does not resolve correct type when using [SerializeReference]

Discussion in 'Editor & General Support' started by kpathiakis_ba, Aug 6, 2023.

  1. kpathiakis_ba

    kpathiakis_ba

    Joined:
    Jan 11, 2022
    Posts:
    5
    Hey everyone,

    I'm currently dealing with a rather interesting serialization situation that took me a while to figure.

    I'm trying to create an Editor for managing custom properties of different value types. The property array is marked with [SerializeReference] in order to make sure that derived properties are being serialized correctly.

    Let's assume the following classes, I'll try to keep everything as compact as possible:

    Code (CSharp):
    1. [Serializable]
    2. public abstract class BaseProperty
    3. {
    4.     [SerializeField]
    5.     public string Name;
    6. }
    7. [Serializable]
    8. public class BoolProperty : BaseProperty
    9. {
    10.     [SerializeField]
    11.     public bool Value;
    12. }
    13.  
    14. public class MyScript: MonoBehaviour
    15. {
    16.     [SerializeReference] // Derived classes should be serialized correctly with this
    17.     public BaseProperty[] Properties; // Lets assume it only contains BoolProperty instances
    18. }
    And their respective Editor/PropertyDrawer classes:

    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(BaseProperty), true)]
    2. public class BasePropertyPropertyDrawer : PropertyDrawer
    3. {
    4.     public override VisualElement CreatePropertyGUI(SerializedProperty property)
    5.     {
    6.         // Associated inspector elements...
    7.  
    8.         // This part never runs after calling serializedObject.ApplyModifiedProperties()
    9.     }
    10. }
    11.  
    12. [CustomEditor(typeof(MyScript))]
    13. public class MyScriptEditor : Editor
    14. {
    15.     private VisualElement _propertyContainer;
    16.  
    17.     public override VisualElement CreateInspectorGUI()
    18.     {
    19.         VisualElement container = new();
    20.  
    21.         _propertyContainer = new();
    22.         container.Add(_propertyContainer);
    23.  
    24.         UpdatePropertyFields(); // This call works like a charm, everything is rendered correctly
    25.         CreateProperty<BoolProperty>(); // New property is added successfully
    26.         UpdatePropertyFields(); // All Recreated elements appear empty, serialization breaks
    27.    
    28.         return container;
    29.     }
    30.  
    31.     private void UpdatePropertyFields()
    32.     {
    33.         _propertyContainer.Clear();
    34.  
    35.         using SerializedProperty serializedProperties = GetSerializedPropertiesArray();
    36.         int length = serializedProperties.arraySize;
    37.         for (int i = 0; i < length; i++)
    38.         {
    39.             using SerializedProperty property = serializedProperties.GetArrayElementAtIndex(i);
    40.             PropertyField propertyField = new(property);
    41.             _propertyContainer.Add(propertyField);
    42.         }
    43.     }
    44.  
    45.     private void CreateProperty<T>()
    46.         where T : BaseProperty, new()
    47.     {
    48.         serializedObject.Update();
    49.  
    50.         // Extend properties array
    51.         using SerializedProperty serializedProperties = GetSerializedPropertiesArray();
    52.         serializedProperties.arraySize++;
    53.  
    54.         // Create new property and apply changes
    55.         using SerializedProperty property = serializedProperties.GetArrayElementAtIndex(serializedProperties.arraySize - 1);
    56.         property.managedReferenceValue = new T();
    57.         serializedObject.ApplyModifiedProperties();
    58.     }
    59.  
    60.     private SerializedProperty GetSerializedPropertiesArray()
    61.         => serializedObject.FindProperty("Properties");
    62. }
    Everything works nicely, up to the point where I try to manually add a new property.
    After successfully creating and adding the new BoolProperty and calling serializedObject.ApplyModifiedProperties(), the associated PropertyField fails to resolve the correct type.

    Remarks:
    - The issue only happens after manually editing the serializedObject. Running UpdatePropertyFields multiple times without editing the target seems to be working just fine.
    - I have verified that the new property is correctly being added to the MonoBehaviour
    - I'm using Unity 2022.3.2f1

    Does anyone have any idea on how to resolve or work around this issue?
    Any advice will be greatly appreciated.

    Many thanks in advance, looking forward to your answers!
     
    Last edited: Aug 7, 2023
  2. kpathiakis_ba

    kpathiakis_ba

    Joined:
    Jan 11, 2022
    Posts:
    5
    TL;DR Creating a PropertyField out of a newly created derived object stored in a [SerializeReference] array of base classes, after calling serializedObject.ApplyModifiedProperties(), breaks.
     
  3. kpathiakis_ba

    kpathiakis_ba

    Joined:
    Jan 11, 2022
    Posts:
    5
    Bump. Any updates from the community experts?