Unity's SerializedProperty not support custom type value setting. This extension use Reflection to get target instance of SerializedProperty in Custom Editor, made value setting of general type is posible.
-
-
Save douduck08/6d3e323b538a741466de00c30aa4b61f to your computer and use it in GitHub Desktop.
| // <author> | |
| // douduck08: https://github.com/douduck08 | |
| // Use Reflection to get instance of Unity's SerializedProperty in Custom Editor. | |
| // Modified codes from 'Unity Answers', in order to apply on nested List<T> or Array. | |
| // | |
| // Original author: HiddenMonk & Johannes Deml | |
| // Ref: http://answers.unity3d.com/questions/627090/convert-serializedproperty-to-custom-class.html | |
| // </author> | |
| using System.Collections; | |
| using System.Linq; | |
| using System.Reflection; | |
| using System.Text.RegularExpressions; | |
| using UnityEditor; | |
| using UnityEngine; | |
| public static class SerializedPropertyExtension | |
| { | |
| static readonly Regex rgx = new Regex(@"\[\d+\]", RegexOptions.Compiled); | |
| public static T GetValue<T>(this SerializedProperty property) where T : class | |
| { | |
| object obj = property.serializedObject.targetObject; | |
| string path = property.propertyPath.Replace(".Array.data", ""); | |
| string[] fieldStructure = path.Split('.'); | |
| for (int i = 0; i < fieldStructure.Length; i++) | |
| { | |
| if (fieldStructure[i].Contains("[")) | |
| { | |
| int index = System.Convert.ToInt32(new string(fieldStructure[i].Where(c => char.IsDigit(c)).ToArray())); | |
| obj = GetFieldValueWithIndex(rgx.Replace(fieldStructure[i], ""), obj, index); | |
| } | |
| else | |
| { | |
| obj = GetFieldValue(fieldStructure[i], obj); | |
| } | |
| } | |
| return (T)obj; | |
| } | |
| public static bool SetValue<T>(this SerializedProperty property, T value) where T : class | |
| { | |
| object obj = property.serializedObject.targetObject; | |
| string path = property.propertyPath.Replace(".Array.data", ""); | |
| string[] fieldStructure = path.Split('.'); | |
| for (int i = 0; i < fieldStructure.Length - 1; i++) | |
| { | |
| if (fieldStructure[i].Contains("[")) | |
| { | |
| int index = System.Convert.ToInt32(new string(fieldStructure[i].Where(c => char.IsDigit(c)).ToArray())); | |
| obj = GetFieldValueWithIndex(rgx.Replace(fieldStructure[i], ""), obj, index); | |
| } | |
| else | |
| { | |
| obj = GetFieldValue(fieldStructure[i], obj); | |
| } | |
| } | |
| string fieldName = fieldStructure.Last(); | |
| if (fieldName.Contains("[")) | |
| { | |
| int index = System.Convert.ToInt32(new string(fieldName.Where(c => char.IsDigit(c)).ToArray())); | |
| return SetFieldValueWithIndex(rgx.Replace(fieldName, ""), obj, index, value); | |
| } | |
| else | |
| { | |
| return SetFieldValue(fieldName, obj, value); | |
| } | |
| } | |
| private static object GetFieldValue(string fieldName, object obj, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) | |
| { | |
| FieldInfo field = obj.GetType().GetField(fieldName, bindings); | |
| if (field != null) | |
| { | |
| return field.GetValue(obj); | |
| } | |
| return default(object); | |
| } | |
| private static object GetFieldValueWithIndex(string fieldName, object obj, int index, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) | |
| { | |
| FieldInfo field = obj.GetType().GetField(fieldName, bindings); | |
| if (field != null) | |
| { | |
| object list = field.GetValue(obj); | |
| if (list.GetType().IsArray) | |
| { | |
| return ((object[])list)[index]; | |
| } | |
| else if (list is IEnumerable) | |
| { | |
| return ((IList)list)[index]; | |
| } | |
| } | |
| return default(object); | |
| } | |
| public static bool SetFieldValue(string fieldName, object obj, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) | |
| { | |
| FieldInfo field = obj.GetType().GetField(fieldName, bindings); | |
| if (field != null) | |
| { | |
| field.SetValue(obj, value); | |
| return true; | |
| } | |
| return false; | |
| } | |
| public static bool SetFieldValueWithIndex(string fieldName, object obj, int index, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) | |
| { | |
| FieldInfo field = obj.GetType().GetField(fieldName, bindings); | |
| if (field != null) | |
| { | |
| object list = field.GetValue(obj); | |
| if (list.GetType().IsArray) | |
| { | |
| ((object[])list)[index] = value; | |
| return true; | |
| } | |
| else if (list is IEnumerable) | |
| { | |
| ((IList)list)[index] = value; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| } |
Is this a typo?
Looks like be yes.
But value and list both should be IEnumerable, so it's still work.
Thanks.
I advise you to move the Regex to a separate static variable to improve performance by about 4 times.
Like this:
private static readonly Regex rgx = new Regex(@"\[\d+\]", RegexOptions.Compiled);
P.S. RegexOptions.Compiled might give a performance improvement, but I'm not sure.
I advise you to move the Regex to a separate static variable to improve performance by about 4 times. Like this:
private static readonly Regex rgx = new Regex(@"\[\d+\]", RegexOptions.Compiled);P.S.RegexOptions.Compiledmight give a performance improvement, but I'm not sure.
Good idea!
You are legend!!!!!!
This gist is exactly what I needed
You can support both value and reference types if you change the GetFieldValueWithIndex() and SetFieldValueWithIndex() implementations:
private static object GetFieldValueWithIndex(string fieldName, object obj, int index, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
object collection = field.GetValue(obj);
if (collection is Array array)
{
return array.GetValue(index);
}
else if (collection is IList list)
{
return list[index];
}
}
return default;
}
public static bool SetFieldValueWithIndex(string fieldName, object obj, int index, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
object collection = field.GetValue(obj);
if (collection is Array array)
{
array.SetValue(value, index);
return true;
}
else if (collection is IList list)
{
list[index] = value;
return true;
}
}
return false;
}
https://gist.github.com/douduck08/6d3e323b538a741466de00c30aa4b61f#file-serializedpropertyextensions-cs-L97
} else if (value is IEnumerable) {Is this a typo?
else if (list is IEnumerable)