using System; using System.Collections; using System.Diagnostics; using System.Text.RegularExpressions; using UnityEngine; using Debug = UnityEngine.Debug; using Object = UnityEngine.Object; using Type = System.Type; #if UNITY_EDITOR using UnityEditor; using System.Reflection; #endif namespace Jovian.Utilities { public static class SerializedObjectUtility { [Conditional("UNITY_EDITOR")] public static void SaveObjectProperties(Object targetObject, params object[] args) { #if UNITY_EDITOR EditorSerializedObjectUtility.SaveObjectProperties(targetObject, args); #endif } } #if UNITY_EDITOR public static class EditorSerializedObjectUtility { //https://answers.unity.com/questions/929293/get-field-type-of-serializedproperty.html public static Type GetTypeFromProperty(SerializedProperty property) { //gets parent type info string[] slices = property.propertyPath.Split('.'); System.Type type = property.serializedObject.targetObject.GetType(); for(int i = 0; i < slices.Length; i++) if (slices[i] == "Array") { i++; //skips "data[x]" type = type.GetElementType(); //gets info on array elements } //gets info on field and its type else type = type.GetField(slices[i], BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance).FieldType; //type is now the type of the property return type; } private static readonly Regex isArrayElementRegex = new Regex("Array.data\\[\\d+\\]$"); public static bool IsPropertyAnArrayElement(SerializedProperty property) { return isArrayElementRegex.IsMatch(property.propertyPath); } public static SerializedProperty GetArrayPropertyWithElementProperty(SerializedProperty property) { SerializedObject serializedObject = property.serializedObject; string propertyPath = property.propertyPath; int arrayDataIndex = propertyPath.LastIndexOf(".Array.data", StringComparison.Ordinal); string propertyPathWithoutArray = propertyPath.Substring(0, arrayDataIndex); int pathDividerIndex = propertyPathWithoutArray.LastIndexOf(".", StringComparison.Ordinal); string parentPropertyName = propertyPathWithoutArray; if(pathDividerIndex != -1) { parentPropertyName = propertyPathWithoutArray.Substring(pathDividerIndex); } return serializedObject.FindProperty(parentPropertyName);; } public static void SaveObjectProperties(Object targetObject, params object[] args) { SerializedObject serializedObject = new SerializedObject(targetObject); for(int i = 0; i < args.Length; i += 2) { var keyArg = args[i]; var keyType = keyArg.GetType(); if((keyType == typeof(string)) == false) { throw new System.NotSupportedException(string.Format("Key must be string. {0} is {1}", args[i], keyType)); } else { var property = serializedObject.FindProperty((string)keyArg); object argValue = args[i + 1]; if(property == null) { throw new System.Exception(string.Format("No property found for key {0}", keyArg)); } if(argValue == null) { property.objectReferenceValue = null; } else { if(property.isArray) { property.arraySize = 0; IEnumerable argArray = (IEnumerable)argValue; IEnumerator enumerator = argArray.GetEnumerator(); int index = 0; while(enumerator.MoveNext()) { property.InsertArrayElementAtIndex(index); var arrayProperty = property.GetArrayElementAtIndex(index); SetSerializedPropertyValue(arrayProperty, enumerator.Current); index++; } } else { SetSerializedPropertyValue(property, argValue); } } } } serializedObject.ApplyModifiedProperties(); } private static void SetSerializedPropertyValue(SerializedProperty property, object value) { if(property == null) { UnityEngine.Debug.LogError("SetSerializedPropertyValue failed, property is null"); return; } switch(property.propertyType) { case SerializedPropertyType.AnimationCurve: property.animationCurveValue = (AnimationCurve)value; break; case SerializedPropertyType.Boolean: property.boolValue = (bool)value; break; case SerializedPropertyType.BoundsInt: property.boundsIntValue = (BoundsInt)value; break; case SerializedPropertyType.Character: property.intValue = (int)(char)value; break; case SerializedPropertyType.Color: { if(value is Color32) { property.colorValue = (Color)(Color32)value; } else { property.colorValue = (Color)value; } break; } case SerializedPropertyType.ExposedReference: case SerializedPropertyType.ObjectReference: property.objectReferenceValue = (Object)value; break; case SerializedPropertyType.Float: property.floatValue = (float)value; break; case SerializedPropertyType.Integer: property.intValue = (int)value; break; case SerializedPropertyType.LayerMask: property.intValue = ((LayerMask)value).value; break; case SerializedPropertyType.Quaternion: property.quaternionValue = (Quaternion)value; break; case SerializedPropertyType.Rect: property.rectValue = (Rect)value; break; case SerializedPropertyType.RectInt: property.rectIntValue = (RectInt)value; break; case SerializedPropertyType.String: property.stringValue = (string)value; break; case SerializedPropertyType.Vector2: property.vector2Value = (Vector2)value; break; case SerializedPropertyType.Vector2Int: property.vector2IntValue = (Vector2Int)value; break; case SerializedPropertyType.Vector3: property.vector3Value = (Vector3)value; break; case SerializedPropertyType.Vector3Int: property.vector3IntValue = (Vector3Int)value; break; case SerializedPropertyType.Vector4: property.vector4Value = (Vector4)value; break; case SerializedPropertyType.Enum: property.enumValueIndex = (int)value; // need to test this // flags??? break; case SerializedPropertyType.Generic: SaveGenericProperty(property, value); break; default: throw new System.NotSupportedException($"PropertyType {property.propertyType} is not supported - yet. Array? {property.isArray} Path: {property.propertyPath}"); } } private static void SaveGenericProperty(SerializedProperty property, object instance) { Type type = instance.GetType(); var fields = type.GetRuntimeFields(); foreach(FieldInfo field in fields) { if(field.IsNotSerialized || field.IsStatic) { continue; } SerializedProperty fieldProperty = property.FindPropertyRelative(field.Name); if(fieldProperty != null) { SetSerializedPropertyValue(property.FindPropertyRelative(field.Name), field.GetValue(instance)); } else { UnityEngine.Debug.Log($"SaveGenericProperty cannot field serializedProperty named '{field.Name}'"); } } } } #endif }