GenerationStatsWindow.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. using System.Linq;
  2. using UnityEditor;
  3. using UnityEngine;
  4. using UnityEngine.UIElements;
  5. namespace DunGen.Editor
  6. {
  7. public class DungeonStatsDisplayWindow : EditorWindow
  8. {
  9. #region Statics
  10. [MenuItem("Window/DunGen/Generation Stats")]
  11. public static void ShowWindow()
  12. {
  13. var window = GetWindow<DungeonStatsDisplayWindow>("Generation Stats");
  14. window.minSize = new Vector2(380, 590);
  15. window.Show();
  16. }
  17. #endregion
  18. private const int WideModeThreshold = 600;
  19. private GenerationStats currentStats;
  20. private bool isGenerating;
  21. private ScrollView tileStatsScrollView;
  22. private VisualElement leftPanel;
  23. private VisualElement rightPanel;
  24. private VisualElement statsContainer;
  25. private Label generatingLabel;
  26. private Label noStatsLabel;
  27. private void OnEnable()
  28. {
  29. DungeonGenerator.OnAnyDungeonGenerationStarted += OnGenerationStarted;
  30. CreateUI();
  31. }
  32. private void OnDisable()
  33. {
  34. DungeonGenerator.OnAnyDungeonGenerationStarted -= OnGenerationStarted;
  35. }
  36. private void CreateUI()
  37. {
  38. var root = rootVisualElement;
  39. // Load stylesheet
  40. var styleSheet = FindStyleSheet();
  41. if (styleSheet != null)
  42. root.styleSheets.Add(styleSheet);
  43. // Create status labels container
  44. var statusContainer = new VisualElement() { name = "StatusContainer" };
  45. root.Add(statusContainer);
  46. generatingLabel = new Label("Generation in progress. Please wait...") { name = "StatusLabel" };
  47. noStatsLabel = new Label("No generation stats available. Generate a dungeon to see statistics.") { name = "StatusLabel" };
  48. statusContainer.Add(generatingLabel);
  49. statusContainer.Add(noStatsLabel);
  50. // Create main container with column flex
  51. var mainContainer = new VisualElement() { name = "MainContainer" };
  52. mainContainer.style.flexDirection = FlexDirection.Column;
  53. mainContainer.style.flexGrow = 1;
  54. root.Add(mainContainer);
  55. // Content container for panels (row or column based on width)
  56. var contentContainer = new VisualElement() { name = "ContentContainer" };
  57. contentContainer.style.flexGrow = 1;
  58. mainContainer.Add(contentContainer);
  59. // Panels
  60. leftPanel = new VisualElement() { name = "LeftPanel" };
  61. rightPanel = new VisualElement() { name = "RightPanel" };
  62. contentContainer.Add(leftPanel);
  63. contentContainer.Add(rightPanel);
  64. CreateLeftPanel();
  65. CreateRightPanel();
  66. // Register callback for window resize
  67. root.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
  68. UpdateUI();
  69. }
  70. private void OnGeometryChanged(GeometryChangedEvent evt)
  71. {
  72. var contentContainer = rootVisualElement.Q("ContentContainer");
  73. // Switch between row and column layout based on width
  74. if (contentContainer != null)
  75. {
  76. bool isWide = evt.newRect.width >= WideModeThreshold;
  77. contentContainer.style.flexDirection = isWide ? FlexDirection.Row : FlexDirection.Column;
  78. if (isWide)
  79. {
  80. leftPanel.style.width = new Length(40, LengthUnit.Percent);
  81. rightPanel.style.width = new Length(60, LengthUnit.Percent);
  82. leftPanel.style.marginRight = 10;
  83. rightPanel.style.marginLeft = 10;
  84. rightPanel.style.marginTop = 0;
  85. // Reset height styles
  86. leftPanel.style.height = StyleKeyword.Auto;
  87. rightPanel.style.height = StyleKeyword.Auto;
  88. }
  89. else
  90. {
  91. // In column mode, use full width
  92. leftPanel.style.width = new Length(100, LengthUnit.Percent);
  93. rightPanel.style.width = new Length(100, LengthUnit.Percent);
  94. leftPanel.style.marginRight = 0;
  95. rightPanel.style.marginLeft = 0;
  96. rightPanel.style.marginTop = 10;
  97. leftPanel.style.height = StyleKeyword.Auto;
  98. rightPanel.style.flexGrow = 1;
  99. }
  100. }
  101. }
  102. private StyleSheet FindStyleSheet()
  103. {
  104. // Find the stylesheet
  105. const string styleSheetName = "GenerationStatsWindow";
  106. var guids = AssetDatabase.FindAssets($"{styleSheetName} t:StyleSheet");
  107. if (guids.Length == 0)
  108. {
  109. Debug.LogWarning($"Could not find {styleSheetName}.uss stylesheet");
  110. return null;
  111. }
  112. var path = AssetDatabase.GUIDToAssetPath(guids[0]);
  113. return AssetDatabase.LoadAssetAtPath<StyleSheet>(path);
  114. }
  115. private void CreateLeftPanel()
  116. {
  117. // Overview section
  118. leftPanel.Add(new Label("Generation Overview") { name = "SectionHeader" });
  119. statsContainer = new VisualElement() { name = "StatsContainer" };
  120. leftPanel.Add(statsContainer);
  121. // Generation Steps section
  122. leftPanel.Add(new Label("Generation Step Times") { name = "SectionHeader" });
  123. var stepsContainer = new VisualElement() { name = "StepsContainer" };
  124. leftPanel.Add(stepsContainer);
  125. }
  126. private void CreateRightPanel()
  127. {
  128. var rightContainer = new VisualElement() { name = "RightContainer" };
  129. rightContainer.style.flexGrow = 1;
  130. rightContainer.style.flexDirection = FlexDirection.Column; // Ensure vertical layout
  131. rightPanel.Add(rightContainer);
  132. rightContainer.Add(new Label("Tile Statistics") { name = "SectionHeader" });
  133. // Create scroll view container that fills remaining space
  134. var scrollContainer = new VisualElement() { name = "ScrollContainer" };
  135. scrollContainer.style.flexGrow = 1;
  136. scrollContainer.style.overflow = Overflow.Hidden;
  137. rightContainer.Add(scrollContainer);
  138. // Create scroll view for tile stats
  139. tileStatsScrollView = new ScrollView(ScrollViewMode.Vertical) { name = "TileStatsScrollView" };
  140. tileStatsScrollView.style.flexGrow = 1;
  141. tileStatsScrollView.style.minHeight = 0;
  142. scrollContainer.Add(tileStatsScrollView);
  143. }
  144. private void UpdateUI()
  145. {
  146. if (rootVisualElement == null)
  147. return;
  148. generatingLabel.style.display = isGenerating ? DisplayStyle.Flex : DisplayStyle.None;
  149. noStatsLabel.style.display = (!isGenerating && currentStats == null) ? DisplayStyle.Flex : DisplayStyle.None;
  150. var mainContainer = rootVisualElement.Q("MainContainer");
  151. mainContainer.style.display = (!isGenerating && currentStats != null) ? DisplayStyle.Flex : DisplayStyle.None;
  152. if (currentStats != null && !isGenerating)
  153. {
  154. UpdateStats();
  155. UpdateTileStats();
  156. }
  157. }
  158. private void UpdateStats()
  159. {
  160. statsContainer.Clear();
  161. AddStatRow("Total Time", $"{currentStats.TotalTime:F2} ms");
  162. AddStatRow("Total Room Count", currentStats.TotalRoomCount.ToString());
  163. AddStatRow("Main Path Rooms", currentStats.MainPathRoomCount.ToString());
  164. AddStatRow("Branch Path Rooms", currentStats.BranchPathRoomCount.ToString());
  165. AddStatRow("Max Branch Depth", currentStats.MaxBranchDepth.ToString());
  166. AddStatRow("Total Retries", currentStats.TotalRetries.ToString());
  167. var stepsContainer = rootVisualElement.Q("StepsContainer");
  168. stepsContainer.Clear();
  169. foreach (var step in currentStats.GenerationStepTimes)
  170. AddStatRow(step.Key.ToString(), $"{step.Value:F2} ms", stepsContainer);
  171. }
  172. private void AddStatRow(string label, string value, VisualElement container = null)
  173. {
  174. container ??= statsContainer;
  175. var row = new VisualElement() { name = "StatRow" };
  176. row.style.flexDirection = FlexDirection.Row;
  177. // Add alternate background based on row index
  178. if (container.Children().Count() % 2 == 1)
  179. row.AddToClassList("alternate");
  180. var labelElement = new Label(label) { name = "StatLabel" };
  181. var valueElement = new Label(value) { name = "StatValue" };
  182. row.Add(labelElement);
  183. row.Add(valueElement);
  184. container.Add(row);
  185. }
  186. private void UpdateTileStats()
  187. {
  188. tileStatsScrollView.Clear();
  189. foreach (var stat in currentStats.GetTileStatistics())
  190. {
  191. var tileContainer = new VisualElement() { name = "TileStatContainer" };
  192. var prefabLabel = new Label(stat.TilePrefab.name) { name = "TilePrefabLabel" };
  193. prefabLabel.AddToClassList("clickable-label");
  194. // Add click handler to select the prefab in Project window
  195. prefabLabel.RegisterCallback<ClickEvent>(_ =>
  196. {
  197. EditorGUIUtility.PingObject(stat.TilePrefab);
  198. Selection.activeObject = stat.TilePrefab;
  199. });
  200. tileContainer.Add(prefabLabel);
  201. tileContainer.Add(new Label("Usage Statistics:") { name = "SubHeader" });
  202. tileContainer.Add(new Label($"Total Uses: {stat.TotalCount}"));
  203. int instantiated = stat.TotalCount - stat.FromPoolCount;
  204. if (stat.FromPoolCount > 0)
  205. {
  206. tileContainer.Add(new Label($"• New Instances: {instantiated}"));
  207. tileContainer.Add(new Label($"• From Pool: {stat.FromPoolCount}"));
  208. }
  209. tileStatsScrollView.Add(tileContainer);
  210. }
  211. }
  212. private void OnGenerationStarted(DungeonGenerator generator)
  213. {
  214. generator.OnGenerationComplete += OnGenerationComplete;
  215. isGenerating = true;
  216. SetStats(null);
  217. }
  218. private void OnGenerationComplete(DungeonGenerator generator)
  219. {
  220. generator.OnGenerationComplete -= OnGenerationComplete;
  221. isGenerating = false;
  222. SetStats(generator.GenerationStats);
  223. }
  224. public void SetStats(GenerationStats stats)
  225. {
  226. currentStats = stats;
  227. UpdateUI();
  228. }
  229. }
  230. }