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; | |
} | |
} |
I love you for writing this
👍
You're a fucking star, and you just earned one too
super useful! thanks alot for sharing this
Than you for sharing epic content!
Very useful script! Thank you very much!
https://gist.github.com/douduck08/6d3e323b538a741466de00c30aa4b61f#file-serializedpropertyextensions-cs-L97
} else if (value is IEnumerable) {
Is this a typo?
else if (list is IEnumerable)
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.Compiled
might 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;
}
I love you for writing this