using UnityEngine; using UnityEditor; using System.Collections.Generic; namespace IntelligentProjectAnalyzer.Serializers { #region Serializable Data Structures /// /// The root object for the serialized prefab JSON. /// Contains the flat list of all unique components and the root of the hierarchy. /// [System.Serializable] public class SerializablePrefab { public List components = new(); public SerializableGameObject hierarchy; } /// /// Represents a single, unique component type found in the prefab. /// Its index in the main list serves as its ID. /// [System.Serializable] public class SerializableComponent { public string type; } /// /// Represents a single GameObject in the prefab's hierarchy. /// This structure is recursive. /// [System.Serializable] public class SerializableGameObject { public string Name; // A list of integers pointing to the indices of components in the SerializablePrefab's main component list. public List ComponentIndices = new(); public List Children = new(); } #endregion /// /// A static class that provides functionality to serialize a Unity GameObject/Prefab /// into a custom JSON format that describes its hierarchy and component structure. /// public static class PrefabSerializer { /// /// A test menu item to allow easy serialization of a selected prefab. /// [MenuItem("Assets/Serialize Selected Prefab to JSON")] public static void SerializeSelectedPrefab() { var selectedObject = Selection.activeObject as GameObject; var json = Serialize(selectedObject); Debug.Log($"--- Serialized Prefab: {selectedObject?.name} ---\n{json}"); } /// /// Serializes a GameObject into a JSON string with a specific format. /// /// The root GameObject of the prefab to serialize. /// A formatted JSON string representing the prefab's structure. public static string Serialize(GameObject prefabRoot) { if (prefabRoot == null) return "{}"; // Ensure we are working with the root prefab asset, not an instance in the scene. if (!PrefabUtility.IsPartOfPrefabAsset(prefabRoot)) { Debug.LogError("The selected object is not a prefab asset. Please select a prefab from the Project window."); return "{}"; } // This dictionary will map a component type name to its unique index. var componentTypeToIndexMap = new Dictionary(); var serializablePrefab = new SerializablePrefab(); // Start the recursive traversal from the root GameObject. serializablePrefab.hierarchy = TraverseHierarchy(prefabRoot.transform, componentTypeToIndexMap, serializablePrefab.components); return JsonUtility.ToJson(serializablePrefab, true); } public static bool TrySerialize(GameObject prefabRoot, out SerializablePrefab serializablePrefab) { serializablePrefab = new SerializablePrefab(); if (prefabRoot == null) return false; // Ensure we are working with the root prefab asset, not an instance in the scene. if (!PrefabUtility.IsPartOfPrefabAsset(prefabRoot)) { Debug.LogError("The selected object is not a prefab asset. Please select a prefab from the Project window."); return false; } // This dictionary will map a component type name to its unique index. var componentTypeToIndexMap = new Dictionary(); // Start the recursive traversal from the root GameObject. serializablePrefab.hierarchy = TraverseHierarchy(prefabRoot.transform, componentTypeToIndexMap, serializablePrefab.components); return serializablePrefab != null; } /// /// Recursively traverses the transform hierarchy to build the serializable structure. /// /// The transform of the GameObject currently being processed. /// The lookup dictionary for component types to their indices. /// The master list of unique component types. /// A SerializableGameObject representing the current transform and its children. private static SerializableGameObject TraverseHierarchy(Transform currentTransform, Dictionary componentMap, List componentList) { var serializableGo = new SerializableGameObject { Name = currentTransform.name }; // Get all components on the current GameObject. var componentsOnGo = currentTransform.GetComponents(); foreach (var component in componentsOnGo) { // Some components can be null if a script is missing. if (component == null) continue; var componentType = component.GetType().FullName; if (componentType == null) { Debug.LogError($"Failed to serialize component on GameObject {currentTransform.name} because its type is null. Skipping this component."); continue; } // If we haven't seen this component type before, add it to our master list. if (!componentMap.ContainsKey(componentType)) { componentMap[componentType] = componentList.Count; componentList.Add(new SerializableComponent { type = componentType }); } // Add the index of this component type to this GameObject's list. serializableGo.ComponentIndices.Add(componentMap[componentType]); } // Recursively process all children. foreach (Transform child in currentTransform) { serializableGo.Children.Add(TraverseHierarchy(child, componentMap, componentList)); } return serializableGo; } } }