Skip to content

Instantly share code, notes, and snippets.

@douduck08
Last active May 16, 2024 18:25
Show Gist options
  • Save douduck08/6d3e323b538a741466de00c30aa4b61f to your computer and use it in GitHub Desktop.
Save douduck08/6d3e323b538a741466de00c30aa4b61f to your computer and use it in GitHub Desktop.
The general GetValue extension of SerializedProperty in Unity Editor.

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.

// <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;
}
}
@BigBlueDraco
Copy link

You are legend!!!!!!

@Nanders
Copy link

Nanders commented May 16, 2024

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;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment