Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only.

    Please, do not make any changes to your username or email addresses at id.unity.com during this transition time.

    It's still possible to reply to existing private message conversations during the migration, but any new replies you post will be missing after the main migration is complete. We'll do our best to migrate these messages in a follow-up step.

    On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live.


    Read our full announcement for more information and let us know if you have any questions.

Feedback Generic type serialization + SerializedObject

Discussion in '2020.1 Beta' started by liortal, Apr 9, 2020.

  1. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    I'm playing with 2020.1 beta, the "generics serializaton" is a nice touch (finally!).
    Is it planned to be supported together with the SerializedObject and SerializedProperty APIs ?

    Right now, in my simple example, i'm trying to iterate over a component that has a generic type as a serialized field. The SerializedProperty returns 'CachedValue`1' as the type and null as the objectReferenceValue.

    Should this work? or am i doing something wrong?

    This is my component BTW:
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. /// <summary>
    6. /// Represents a value that is cached in PlayerPrefs.
    7. /// </summary>
    8. /// <typeparam name="T"></typeparam>
    9. [Serializable]
    10. public class CachedValue<T>
    11. {
    12.     [SerializeField]
    13.     private T myValue;
    14.    
    15.     // TODO: more code here - flush to PlayerPrefs when dirty.
    16. }
    17.  
    18. public class LiorScript : MonoBehaviour
    19. {
    20.     public CachedValue<int> intCache;
    21.     public CachedValue<string> stringCache;
    22.     public CachedValue<Vector3> vector3Cache;
    23. }
     

    Attached Files:

  2. jasonatkaruna

    jasonatkaruna

    Joined:
    Feb 26, 2019
    Posts:
    64
    SerializedProperty.objectReferenceValue
    is to refer to an object that inherits from
    UnityEngine.Object
    , so that should be null.

    Man it's a bummer that it's not returning the generic type arguments in the name. If not, you can use a script to get the
    System.Object
    value by walking down the property path from the
    SerializedObject.target
    . Once you get that, it should be as simple as
    obj.GetType()
    .

    It's pretty useful to have a "GetObjectValue" extension method for Serialized Properties. Here's a snippet of the one I use:


    Code (CSharp):
    1. private static readonly Regex ArrayPathRegex = new Regex(
    2.     @"(?<arrayName>\w+)\[(?<index>\d+)\]",
    3.     RegexOptions.Compiled);
    4.  
    5. public static object GetValue(this SerializedProperty property,
    6.     Func<IEnumerable<string>, IEnumerable<string>> modifier = null)
    7. {
    8.     IEnumerable<string> path = property.propertyPath.Replace(".Array.data[", "[").Split('.');
    9.     if (modifier != null)
    10.     {
    11.         path = modifier(path);
    12.     }
    13.     var target = (object)property.serializedObject.targetObject;
    14.     return GetValueRecur(target, path);
    15. }
    16.  
    17. private static object GetValueRecur(object target, IEnumerable<string> path)
    18. {
    19.     if (target == null) throw new ArgumentNullException(nameof(target));
    20.  
    21.     var head = path.FirstOrDefault();
    22.     if (head == null)
    23.     {
    24.          return target;
    25.     }
    26.  
    27.     var arrayMatch = ArrayPathRegex.Match(head);
    28.     if (arrayMatch != null)
    29.     {
    30.         head = arrayMatch.Groups["arrayName"].Value;
    31.         var index = int.Parse(arrayMatch.Groups["index"].Value);
    32.         var field = target.GetType().GetField(head, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    33.         var array = field.GetValue(target) as IEnumerable;
    34.         target = array.ElementAtOrDefault(index);
    35.     }
    36.     else
    37.     {
    38.         target = target.GetType().GetField(head).GetValue(target);
    39.     }
    40.     return GetValueRecur(target, path.Skip(1));
    41. }
    42.  
    The
    Skip
    and
    ElementAt
    are
    IEnumerable
    extensions. You can browse them here: https://gitlab.com/jasonboukheir/unity-editor-extensions. You can make a simpler version that doesn't account for arrays.