123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- using UnityEngine;
- using UnityEditor;
- using System.Collections.Generic;
- namespace IntelligentProjectAnalyzer.Serializers
- {
- #region Serializable Data Structures
- /// <summary>
- /// The root object for the serialized prefab JSON.
- /// Contains the flat list of all unique components and the root of the hierarchy.
- /// </summary>
- [System.Serializable]
- public class SerializablePrefab
- {
- public List<SerializableComponent> components = new();
- public SerializableGameObject hierarchy;
- }
- /// <summary>
- /// Represents a single, unique component type found in the prefab.
- /// Its index in the main list serves as its ID.
- /// </summary>
- [System.Serializable]
- public class SerializableComponent
- {
- public string type;
- }
- /// <summary>
- /// Represents a single GameObject in the prefab's hierarchy.
- /// This structure is recursive.
- /// </summary>
- [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<int> ComponentIndices = new();
- public List<SerializableGameObject> Children = new();
- }
- #endregion
- /// <summary>
- /// A static class that provides functionality to serialize a Unity GameObject/Prefab
- /// into a custom JSON format that describes its hierarchy and component structure.
- /// </summary>
- public static class PrefabSerializer
- {
- /// <summary>
- /// A test menu item to allow easy serialization of a selected prefab.
- /// </summary>
- [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}");
- }
- /// <summary>
- /// Serializes a GameObject into a JSON string with a specific format.
- /// </summary>
- /// <param name="prefabRoot">The root GameObject of the prefab to serialize.</param>
- /// <returns>A formatted JSON string representing the prefab's structure.</returns>
- 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<string, int>();
- 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<string, int>();
- // Start the recursive traversal from the root GameObject.
- serializablePrefab.hierarchy = TraverseHierarchy(prefabRoot.transform, componentTypeToIndexMap, serializablePrefab.components);
-
- return serializablePrefab != null;
- }
- /// <summary>
- /// Recursively traverses the transform hierarchy to build the serializable structure.
- /// </summary>
- /// <param name="currentTransform">The transform of the GameObject currently being processed.</param>
- /// <param name="componentMap">The lookup dictionary for component types to their indices.</param>
- /// <param name="componentList">The master list of unique component types.</param>
- /// <returns>A SerializableGameObject representing the current transform and its children.</returns>
- private static SerializableGameObject TraverseHierarchy(Transform currentTransform, Dictionary<string, int> componentMap, List<SerializableComponent> componentList)
- {
- var serializableGo = new SerializableGameObject
- {
- Name = currentTransform.name
- };
- // Get all components on the current GameObject.
- var componentsOnGo = currentTransform.GetComponents<Component>();
- 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;
- }
- }
- }
|