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:
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Represents a value that is cached in PlayerPrefs.
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
public class CachedValue<T>
{
[SerializeField]
private T myValue;
// TODO: more code here - flush to PlayerPrefs when dirty.
}
public class LiorScript : MonoBehaviour
{
public CachedValue<int> intCache;
public CachedValue<string> stringCache;
public CachedValue<Vector3> vector3Cache;
}
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:
private static readonly Regex ArrayPathRegex = new Regex(
@"(?<arrayName>\w+)\[(?<index>\d+)\]",
RegexOptions.Compiled);
public static object GetValue(this SerializedProperty property,
Func<IEnumerable<string>, IEnumerable<string>> modifier = null)
{
IEnumerable<string> path = property.propertyPath.Replace(".Array.data[", "[").Split('.');
if (modifier != null)
{
path = modifier(path);
}
var target = (object)property.serializedObject.targetObject;
return GetValueRecur(target, path);
}
private static object GetValueRecur(object target, IEnumerable<string> path)
{
if (target == null) throw new ArgumentNullException(nameof(target));
var head = path.FirstOrDefault();
if (head == null)
{
return target;
}
var arrayMatch = ArrayPathRegex.Match(head);
if (arrayMatch != null)
{
head = arrayMatch.Groups["arrayName"].Value;
var index = int.Parse(arrayMatch.Groups["index"].Value);
var field = target.GetType().GetField(head, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
var array = field.GetValue(target) as IEnumerable;
target = array.ElementAtOrDefault(index);
}
else
{
target = target.GetType().GetField(head).GetValue(target);
}
return GetValueRecur(target, path.Skip(1));
}
The Skip
and ElementAt
are IEnumerable
extensions. You can browse them here: Jason Elie Bou Kheir / Unity Editor Extensions · GitLab. You can make a simpler version that doesn’t account for arrays.