Feat: Generalize GetComponentData using reflection
parent
cbe9b3a2f0
commit
9b11224357
|
|
@ -2182,39 +2182,138 @@ namespace UnityMcpBridge.Editor.Tools
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a serializable representation of a Component.
|
/// Creates a serializable representation of a Component, attempting to serialize
|
||||||
/// TODO: Add property serialization.
|
/// public properties and fields using reflection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static object GetComponentData(Component c)
|
private static object GetComponentData(Component c)
|
||||||
{
|
{
|
||||||
if (c == null)
|
if (c == null) return null;
|
||||||
return null;
|
|
||||||
var data = new Dictionary<string, object>
|
var data = new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
{ "typeName", c.GetType().FullName },
|
{ "typeName", c.GetType().FullName },
|
||||||
{ "instanceID", c.GetInstanceID() },
|
{ "instanceID", c.GetInstanceID() }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Attempt to serialize public properties/fields (can be noisy/complex)
|
var serializableProperties = new Dictionary<string, object>();
|
||||||
/*
|
Type componentType = c.GetType();
|
||||||
try {
|
// Include NonPublic flags for fields, keep Public for properties initially
|
||||||
var properties = new Dictionary<string, object>();
|
BindingFlags fieldFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
|
||||||
var type = c.GetType();
|
BindingFlags propFlags = BindingFlags.Public | BindingFlags.Instance;
|
||||||
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
|
|
||||||
|
// Process Properties (Still only public for properties)
|
||||||
foreach (var prop in type.GetProperties(flags).Where(p => p.CanRead && p.GetIndexParameters().Length == 0)) {
|
// Using propFlags here
|
||||||
try { properties[prop.Name] = prop.GetValue(c); } catch { }
|
foreach (var propInfo in componentType.GetProperties(propFlags))
|
||||||
|
{
|
||||||
|
// Skip indexers and write-only properties, and skip the transform property as it's handled by GetGameObjectData
|
||||||
|
if (!propInfo.CanRead || propInfo.GetIndexParameters().Length > 0 || propInfo.Name == "transform") continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object value = propInfo.GetValue(c);
|
||||||
|
string propName = propInfo.Name;
|
||||||
|
Type propType = propInfo.PropertyType;
|
||||||
|
|
||||||
|
AddSerializableValue(serializableProperties, propName, propType, value);
|
||||||
}
|
}
|
||||||
foreach (var field in type.GetFields(flags)) {
|
catch (Exception ex)
|
||||||
try { properties[field.Name] = field.GetValue(c); } catch { }
|
{
|
||||||
|
Debug.LogWarning($"Could not read property {propInfo.Name} on {componentType.Name}: {ex.Message}");
|
||||||
}
|
}
|
||||||
data["properties"] = properties;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
data["propertiesError"] = ex.Message;
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
// Process Fields (Include NonPublic)
|
||||||
|
// Using fieldFlags here
|
||||||
|
foreach (var fieldInfo in componentType.GetFields(fieldFlags))
|
||||||
|
{
|
||||||
|
// Skip backing fields for properties (common pattern)
|
||||||
|
if (fieldInfo.Name.EndsWith("k__BackingField")) continue;
|
||||||
|
|
||||||
|
// Only include public fields or non-public fields with [SerializeField]
|
||||||
|
// Check if the field is explicitly marked with SerializeField or if it's public
|
||||||
|
bool isSerializable = fieldInfo.IsPublic || fieldInfo.IsDefined(typeof(SerializeField), inherit: false); // inherit: false is typical for SerializeField
|
||||||
|
|
||||||
|
if (!isSerializable) continue; // Skip if not public and not explicitly serialized
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object value = fieldInfo.GetValue(c);
|
||||||
|
string fieldName = fieldInfo.Name;
|
||||||
|
Type fieldType = fieldInfo.FieldType;
|
||||||
|
|
||||||
|
AddSerializableValue(serializableProperties, fieldName, fieldType, value);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Could not read field {fieldInfo.Name} on {componentType.Name}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serializableProperties.Count > 0)
|
||||||
|
{
|
||||||
|
data["properties"] = serializableProperties; // Add the collected properties
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to decide how to serialize different types
|
||||||
|
private static void AddSerializableValue(Dictionary<string, object> dict, string name, Type type, object value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
dict[name] = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Primitives & Enums
|
||||||
|
if (type.IsPrimitive || type.IsEnum || type == typeof(string))
|
||||||
|
{
|
||||||
|
dict[name] = value;
|
||||||
|
}
|
||||||
|
// Known Unity Structs (add more as needed: Rect, Bounds, etc.)
|
||||||
|
else if (type == typeof(Vector2)) { var v = (Vector2)value; dict[name] = new { v.x, v.y }; }
|
||||||
|
else if (type == typeof(Vector3)) { var v = (Vector3)value; dict[name] = new { v.x, v.y, v.z }; }
|
||||||
|
else if (type == typeof(Vector4)) { var v = (Vector4)value; dict[name] = new { v.x, v.y, v.z, v.w }; }
|
||||||
|
else if (type == typeof(Quaternion)) { var q = (Quaternion)value; dict[name] = new { x = q.eulerAngles.x, y = q.eulerAngles.y, z = q.eulerAngles.z }; } // Serialize as Euler angles for readability
|
||||||
|
else if (type == typeof(Color)) { var c = (Color)value; dict[name] = new { c.r, c.g, c.b, c.a }; }
|
||||||
|
// UnityEngine.Object References
|
||||||
|
else if (typeof(UnityEngine.Object).IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
var obj = value as UnityEngine.Object;
|
||||||
|
if (obj != null) {
|
||||||
|
// Use dynamic or a helper class for flexible properties if adding assetPath
|
||||||
|
var refData = new Dictionary<string, object> {
|
||||||
|
{ "name", obj.name },
|
||||||
|
{ "instanceID", obj.GetInstanceID() },
|
||||||
|
{ "typeName", obj.GetType().FullName }
|
||||||
|
};
|
||||||
|
string assetPath = AssetDatabase.GetAssetPath(obj);
|
||||||
|
if (!string.IsNullOrEmpty(assetPath)) {
|
||||||
|
refData["assetPath"] = assetPath;
|
||||||
|
}
|
||||||
|
dict[name] = refData;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dict[name] = null; // The object reference is null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add handling for basic Lists/Arrays of primitives? (Example for List<string>)
|
||||||
|
else if (type == typeof(List<string>)) {
|
||||||
|
dict[name] = value as List<string>; // Directly serializable
|
||||||
|
}
|
||||||
|
else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) {
|
||||||
|
// Could attempt to serialize lists of primitives/structs/references here if needed
|
||||||
|
dict[name] = $"[Skipped List<{type.GetGenericArguments()[0].Name}>]";
|
||||||
|
}
|
||||||
|
else if (type.IsArray) {
|
||||||
|
dict[name] = $"[Skipped Array<{type.GetElementType().Name}>]";
|
||||||
|
}
|
||||||
|
// Skip other complex types for now
|
||||||
|
else {
|
||||||
|
dict[name] = $"[Skipped complex type: {type.FullName}]";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue