SceneParser.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. using System.IO;
  2. using System.Linq;
  3. using UnityEditor;
  4. using UnityEngine;
  5. using Newtonsoft.Json;
  6. using AssetBank.Editor.Data;
  7. using System.Collections.Generic;
  8. using UnityEditor.SceneManagement;
  9. using UnityEngine.SceneManagement;
  10. using AssetBank.Editor.SceneSchema.Internal;
  11. namespace AssetBank.Editor.SceneSchema
  12. {
  13. public static class SceneParser
  14. {
  15. public static void Process(string scenePath, string savePath, out string output, out string error)
  16. {
  17. output = null;
  18. error = null;
  19. if (string.IsNullOrEmpty(scenePath) || !File.Exists(scenePath))
  20. {
  21. error = $"Scene path is invalid or does not exist. Scene path: {scenePath}";
  22. return;
  23. }
  24. var originalScene = SceneManager.GetActiveScene().path;
  25. try
  26. {
  27. var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
  28. var sceneData = new SceneData();
  29. var prefabList = new List<string>();
  30. var rootGameObjects = scene.GetRootGameObjects();
  31. foreach (var rootGo in rootGameObjects)
  32. {
  33. var goData = ParseGameObjectRecursive(rootGo, prefabList);
  34. if (goData != null)
  35. {
  36. sceneData.Hierarchy.Add(goData);
  37. }
  38. }
  39. sceneData.PrefabsInScene = prefabList;
  40. var json = JsonConvert.SerializeObject(sceneData, new JsonSerializerSettings
  41. {
  42. Formatting = Formatting.None,
  43. NullValueHandling = NullValueHandling.Ignore,
  44. ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
  45. });
  46. File.WriteAllText(savePath, json);
  47. output = $"Successfully processed scene '{scenePath}' and saved JSON to '{savePath}'";
  48. }
  49. catch (System.Exception e)
  50. {
  51. error = $"Failed to process scene '{scenePath}'. Error: \n{e}";
  52. }
  53. finally
  54. {
  55. if (!string.IsNullOrEmpty(originalScene))
  56. {
  57. EditorSceneManager.OpenScene(originalScene);
  58. }
  59. }
  60. }
  61. private static GameObjectData ParseGameObjectRecursive(GameObject go, List<string> prefabList)
  62. {
  63. if (go == null) return null;
  64. var goData = new GameObjectData
  65. {
  66. Enabled = go.activeSelf,
  67. Name = go.name,
  68. Tag = go.tag,
  69. Layer = go.layer,
  70. AnchorId = EditorUtilities.GetLocalFileID(go).ToString()
  71. };
  72. var isPrefab = PrefabUtility.IsPartOfPrefabInstance(go);
  73. var isNewlyAdded = isPrefab && PrefabUtility.GetCorrespondingObjectFromSource(go) == null;
  74. // Case A: Regular GameObject or a new object added to a prefab instance.
  75. if (!isPrefab || isNewlyAdded)
  76. {
  77. foreach (var component in go.GetComponents<Component>())
  78. {
  79. if (component == null) continue;
  80. goData.Components.Add(ParseComponent(component));
  81. }
  82. }
  83. // Case B & C: Object is part of a prefab (root or child).
  84. else
  85. {
  86. // Handle prefab root
  87. if (PrefabUtility.GetNearestPrefabInstanceRoot(go) == go)
  88. {
  89. var prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(go);
  90. if (prefabAsset != null)
  91. {
  92. var prefabGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(prefabAsset));
  93. if (!prefabList.Contains(prefabGuid))
  94. {
  95. prefabList.Add(prefabGuid);
  96. }
  97. goData.PrefabIndex = prefabList.IndexOf(prefabGuid);
  98. }
  99. }
  100. // Handle added/removed components for this specific GameObject
  101. goData.AddedComponents = PrefabUtility.GetAddedComponents(go)
  102. .Select(c => ParseComponent(c.instanceComponent))
  103. .ToList();
  104. goData.RemovedComponentAnchorIds = PrefabUtility.GetRemovedComponents(go)
  105. .Select(c => EditorUtilities.GetLocalFileID(c.assetComponent).ToString())
  106. .ToList();
  107. }
  108. // Recursively parse children and prune empty nodes
  109. foreach (Transform child in go.transform)
  110. {
  111. var childData = ParseGameObjectRecursive(child.gameObject, prefabList);
  112. if (childData != null)
  113. {
  114. goData.Children.Add(childData);
  115. }
  116. }
  117. // Pruning logic: If a prefab node has no overrides and no new children, discard it.
  118. var isOriginalPrefabPart = isPrefab && !isNewlyAdded;
  119. if (isOriginalPrefabPart)
  120. {
  121. var hasOverrides = goData.AddedComponents.Any() || goData.RemovedComponentAnchorIds.Any();
  122. var hasChildrenWithOverrides = goData.Children.Any();
  123. var isRoot = goData.PrefabIndex.HasValue;
  124. // A non-root part of a prefab is empty if it has no overrides of its own and no children with overrides.
  125. if (!isRoot && !hasOverrides && !hasChildrenWithOverrides)
  126. {
  127. return null;
  128. }
  129. }
  130. return goData;
  131. }
  132. private static ComponentData ParseComponent(Component component)
  133. {
  134. var componentData = new ComponentData
  135. {
  136. Name = component.GetType().Name,
  137. AnchorId = EditorUtilities.GetLocalFileID(component).ToString()
  138. };
  139. if (component is MonoBehaviour monoBehaviour)
  140. {
  141. var fullTypeName = monoBehaviour.GetType().FullName;
  142. if (fullTypeName != null && !fullTypeName.StartsWith("UnityEngine."))
  143. {
  144. componentData.Fields = MonoBehaviourSerializer.Serialize(monoBehaviour);
  145. }
  146. }
  147. return componentData;
  148. }
  149. }
  150. }