PrefabSerializer.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. using UnityEngine;
  2. using UnityEditor;
  3. using System.Collections.Generic;
  4. namespace IntelligentProjectAnalyzer.Serializers
  5. {
  6. #region Serializable Data Structures
  7. /// <summary>
  8. /// The root object for the serialized prefab JSON.
  9. /// Contains the flat list of all unique components and the root of the hierarchy.
  10. /// </summary>
  11. [System.Serializable]
  12. public class SerializablePrefab
  13. {
  14. public List<SerializableComponent> components = new();
  15. public SerializableGameObject hierarchy;
  16. }
  17. /// <summary>
  18. /// Represents a single, unique component type found in the prefab.
  19. /// Its index in the main list serves as its ID.
  20. /// </summary>
  21. [System.Serializable]
  22. public class SerializableComponent
  23. {
  24. public string type;
  25. }
  26. /// <summary>
  27. /// Represents a single GameObject in the prefab's hierarchy.
  28. /// This structure is recursive.
  29. /// </summary>
  30. [System.Serializable]
  31. public class SerializableGameObject
  32. {
  33. public string Name;
  34. // A list of integers pointing to the indices of components in the SerializablePrefab's main component list.
  35. public List<int> ComponentIndices = new();
  36. public List<SerializableGameObject> Children = new();
  37. }
  38. #endregion
  39. /// <summary>
  40. /// A static class that provides functionality to serialize a Unity GameObject/Prefab
  41. /// into a custom JSON format that describes its hierarchy and component structure.
  42. /// </summary>
  43. public static class PrefabSerializer
  44. {
  45. /// <summary>
  46. /// A test menu item to allow easy serialization of a selected prefab.
  47. /// </summary>
  48. [MenuItem("Assets/Serialize Selected Prefab to JSON")]
  49. public static void SerializeSelectedPrefab()
  50. {
  51. var selectedObject = Selection.activeObject as GameObject;
  52. var json = Serialize(selectedObject);
  53. Debug.Log($"--- Serialized Prefab: {selectedObject?.name} ---\n{json}");
  54. }
  55. /// <summary>
  56. /// Serializes a GameObject into a JSON string with a specific format.
  57. /// </summary>
  58. /// <param name="prefabRoot">The root GameObject of the prefab to serialize.</param>
  59. /// <returns>A formatted JSON string representing the prefab's structure.</returns>
  60. public static string Serialize(GameObject prefabRoot)
  61. {
  62. if (prefabRoot == null) return "{}";
  63. // Ensure we are working with the root prefab asset, not an instance in the scene.
  64. if (!PrefabUtility.IsPartOfPrefabAsset(prefabRoot))
  65. {
  66. Debug.LogError("The selected object is not a prefab asset. Please select a prefab from the Project window.");
  67. return "{}";
  68. }
  69. // This dictionary will map a component type name to its unique index.
  70. var componentTypeToIndexMap = new Dictionary<string, int>();
  71. var serializablePrefab = new SerializablePrefab();
  72. // Start the recursive traversal from the root GameObject.
  73. serializablePrefab.hierarchy = TraverseHierarchy(prefabRoot.transform, componentTypeToIndexMap, serializablePrefab.components);
  74. return JsonUtility.ToJson(serializablePrefab, true);
  75. }
  76. public static bool TrySerialize(GameObject prefabRoot, out SerializablePrefab serializablePrefab)
  77. {
  78. serializablePrefab = new SerializablePrefab();
  79. if (prefabRoot == null) return false;
  80. // Ensure we are working with the root prefab asset, not an instance in the scene.
  81. if (!PrefabUtility.IsPartOfPrefabAsset(prefabRoot))
  82. {
  83. Debug.LogError("The selected object is not a prefab asset. Please select a prefab from the Project window.");
  84. return false;
  85. }
  86. // This dictionary will map a component type name to its unique index.
  87. var componentTypeToIndexMap = new Dictionary<string, int>();
  88. // Start the recursive traversal from the root GameObject.
  89. serializablePrefab.hierarchy = TraverseHierarchy(prefabRoot.transform, componentTypeToIndexMap, serializablePrefab.components);
  90. return serializablePrefab != null;
  91. }
  92. /// <summary>
  93. /// Recursively traverses the transform hierarchy to build the serializable structure.
  94. /// </summary>
  95. /// <param name="currentTransform">The transform of the GameObject currently being processed.</param>
  96. /// <param name="componentMap">The lookup dictionary for component types to their indices.</param>
  97. /// <param name="componentList">The master list of unique component types.</param>
  98. /// <returns>A SerializableGameObject representing the current transform and its children.</returns>
  99. private static SerializableGameObject TraverseHierarchy(Transform currentTransform, Dictionary<string, int> componentMap, List<SerializableComponent> componentList)
  100. {
  101. var serializableGo = new SerializableGameObject
  102. {
  103. Name = currentTransform.name
  104. };
  105. // Get all components on the current GameObject.
  106. var componentsOnGo = currentTransform.GetComponents<Component>();
  107. foreach (var component in componentsOnGo)
  108. {
  109. // Some components can be null if a script is missing.
  110. if (component == null) continue;
  111. var componentType = component.GetType().FullName;
  112. if (componentType == null)
  113. {
  114. Debug.LogError($"Failed to serialize component on GameObject {currentTransform.name} because its type is null. Skipping this component.");
  115. continue;
  116. }
  117. // If we haven't seen this component type before, add it to our master list.
  118. if (!componentMap.ContainsKey(componentType))
  119. {
  120. componentMap[componentType] = componentList.Count;
  121. componentList.Add(new SerializableComponent { type = componentType });
  122. }
  123. // Add the index of this component type to this GameObject's list.
  124. serializableGo.ComponentIndices.Add(componentMap[componentType]);
  125. }
  126. // Recursively process all children.
  127. foreach (Transform child in currentTransform)
  128. {
  129. serializableGo.Children.Add(TraverseHierarchy(child, componentMap, componentList));
  130. }
  131. return serializableGo;
  132. }
  133. }
  134. }