using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using Newtonsoft.Json; using AssetBank.Editor.Data; using System.Collections.Generic; using UnityEditor.SceneManagement; using UnityEngine.SceneManagement; using AssetBank.Editor.SceneSchema.Internal; namespace AssetBank.Editor.SceneSchema { public static class SceneParser { public static void Process(string scenePath, string savePath, out string output, out string error) { output = null; error = null; if (string.IsNullOrEmpty(scenePath) || !File.Exists(scenePath)) { error = $"Scene path is invalid or does not exist. Scene path: {scenePath}"; return; } var originalScene = SceneManager.GetActiveScene().path; try { var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single); var sceneData = new SceneData(); var prefabList = new List(); var rootGameObjects = scene.GetRootGameObjects(); foreach (var rootGo in rootGameObjects) { var goData = ParseGameObjectRecursive(rootGo, prefabList); if (goData != null) { sceneData.Hierarchy.Add(goData); } } sceneData.PrefabsInScene = prefabList; var json = JsonConvert.SerializeObject(sceneData, new JsonSerializerSettings { Formatting = Formatting.None, NullValueHandling = NullValueHandling.Ignore, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, }); File.WriteAllText(savePath, json); output = $"Successfully processed scene '{scenePath}' and saved JSON to '{savePath}'"; } catch (System.Exception e) { error = $"Failed to process scene '{scenePath}'. Error: \n{e}"; } finally { if (!string.IsNullOrEmpty(originalScene)) { EditorSceneManager.OpenScene(originalScene); } } } private static GameObjectData ParseGameObjectRecursive(GameObject go, List prefabList) { if (go == null) return null; var goData = new GameObjectData { Enabled = go.activeSelf, Name = go.name, Tag = go.tag, Layer = go.layer, AnchorId = EditorUtilities.GetLocalFileID(go).ToString() }; var isPrefab = PrefabUtility.IsPartOfPrefabInstance(go); var isNewlyAdded = isPrefab && PrefabUtility.GetCorrespondingObjectFromSource(go) == null; // Case A: Regular GameObject or a new object added to a prefab instance. if (!isPrefab || isNewlyAdded) { foreach (var component in go.GetComponents()) { if (component == null) continue; goData.Components.Add(ParseComponent(component)); } } // Case B & C: Object is part of a prefab (root or child). else { // Handle prefab root if (PrefabUtility.GetNearestPrefabInstanceRoot(go) == go) { var prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(go); if (prefabAsset != null) { var prefabGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(prefabAsset)); if (!prefabList.Contains(prefabGuid)) { prefabList.Add(prefabGuid); } goData.PrefabIndex = prefabList.IndexOf(prefabGuid); } } // Handle added/removed components for this specific GameObject goData.AddedComponents = PrefabUtility.GetAddedComponents(go) .Select(c => ParseComponent(c.instanceComponent)) .ToList(); goData.RemovedComponentAnchorIds = PrefabUtility.GetRemovedComponents(go) .Select(c => EditorUtilities.GetLocalFileID(c.assetComponent).ToString()) .ToList(); } // Recursively parse children and prune empty nodes foreach (Transform child in go.transform) { var childData = ParseGameObjectRecursive(child.gameObject, prefabList); if (childData != null) { goData.Children.Add(childData); } } // Pruning logic: If a prefab node has no overrides and no new children, discard it. var isOriginalPrefabPart = isPrefab && !isNewlyAdded; if (isOriginalPrefabPart) { var hasOverrides = goData.AddedComponents.Any() || goData.RemovedComponentAnchorIds.Any(); var hasChildrenWithOverrides = goData.Children.Any(); var isRoot = goData.PrefabIndex.HasValue; // A non-root part of a prefab is empty if it has no overrides of its own and no children with overrides. if (!isRoot && !hasOverrides && !hasChildrenWithOverrides) { return null; } } return goData; } private static ComponentData ParseComponent(Component component) { var componentData = new ComponentData { Name = component.GetType().Name, AnchorId = EditorUtilities.GetLocalFileID(component).ToString() }; if (component is MonoBehaviour monoBehaviour) { var fullTypeName = monoBehaviour.GetType().FullName; if (fullTypeName != null && !fullTypeName.StartsWith("UnityEngine.")) { componentData.Fields = MonoBehaviourSerializer.Serialize(monoBehaviour); } } return componentData; } } }