DunGenSettingsInspector.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. using DunGen.Editor.Windows;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor;
  5. using UnityEngine;
  6. namespace DunGen.Editor.Inspectors
  7. {
  8. [CustomEditor(typeof(DunGenSettings))]
  9. public sealed class DunGenSettingsInspector : UnityEditor.Editor
  10. {
  11. private static class Label
  12. {
  13. public static readonly GUIContent IgnoreSpriteBounds = new GUIContent("Ignore Sprite Bounds", "If true, sprite components will be ignored when calculating bounding volumes for tiles");
  14. public static readonly GUIContent RecalculateTileBoundsOnSave = new GUIContent("Recalculate On Save", "If true, tile bounds will automatically be recalculated every time the tile is saved. Otherwise, bounds must be recalculated manually in the Tile component inspector");
  15. public static readonly GUIContent BroadphaseSettings = new GUIContent("Collision Broadphase", "The settings to use for broadphase collision detection");
  16. public static readonly GUIContent TilePooling = new GUIContent("Enable Tile Pooling", "If true, tile instances will be re-used instead of being destroyed, improving generation performance at the cost of increased memory consumption");
  17. public static readonly GUIContent DisplayFailureReportWindow = new GUIContent("Display Failure Report Window", "If true, a window will be displayed when a generation failure occurs, allowing you to inspect the failure report");
  18. public static readonly GUIContent CheckForRemovedFiles = new GUIContent("Check for Unused Files", "DunGen will check if any old files from previous versions are still present in the DunGen directory and will present a list of files to potentially delete");
  19. public static readonly GUIContent CleanDunGenDirectory = new GUIContent("Remove Unused Files", "Removes any DunGen files left over from previous versions that are no longer in use");
  20. }
  21. private SerializedProperty ignoreSpriteBounds;
  22. private SerializedProperty recalculateTileBoundsOnSave;
  23. private SerializedProperty broadphaseSettings;
  24. private SerializedProperty enableTilePooling;
  25. private SerializedProperty displayFailureReportWindow;
  26. private SerializedProperty checkForUnusedFiles;
  27. private void OnEnable()
  28. {
  29. ignoreSpriteBounds = serializedObject.FindProperty(nameof(DunGenSettings.BoundsCalculationsIgnoreSprites));
  30. recalculateTileBoundsOnSave = serializedObject.FindProperty(nameof(DunGenSettings.RecalculateTileBoundsOnSave));
  31. broadphaseSettings = serializedObject.FindProperty(nameof(DunGenSettings.BroadphaseSettings));
  32. enableTilePooling = serializedObject.FindProperty(nameof(DunGenSettings.EnableTilePooling));
  33. displayFailureReportWindow = serializedObject.FindProperty(nameof(DunGenSettings.DisplayFailureReportWindow));
  34. checkForUnusedFiles = serializedObject.FindProperty(nameof(DunGenSettings.CheckForUnusedFiles));
  35. }
  36. public override void OnInspectorGUI()
  37. {
  38. serializedObject.Update();
  39. DrawGeneralProperties();
  40. EditorGUILayout.Space();
  41. DrawOptimizationProperties();
  42. EditorGUILayout.Space();
  43. DrawBoundsProperties();
  44. serializedObject.ApplyModifiedProperties();
  45. }
  46. private void DrawGeneralProperties()
  47. {
  48. var obj = target as DunGenSettings;
  49. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  50. {
  51. EditorGUILayout.LabelField("General", EditorStyles.boldLabel);
  52. EditorGUI.BeginDisabledGroup(true);
  53. EditorGUILayout.ObjectField("Default Socket", obj.DefaultSocket, typeof(DoorwaySocket), false);
  54. EditorGUI.EndDisabledGroup();
  55. EditorGUILayout.PropertyField(displayFailureReportWindow, Label.DisplayFailureReportWindow);
  56. EditorGUILayout.PropertyField(checkForUnusedFiles, Label.CheckForRemovedFiles);
  57. if (GUILayout.Button(Label.CleanDunGenDirectory))
  58. DunGenFolderCleaningWindow.CleanDunGenDirectory();
  59. }
  60. EditorGUILayout.EndVertical();
  61. }
  62. private void DrawOptimizationProperties()
  63. {
  64. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  65. {
  66. EditorGUILayout.LabelField("Optimization", EditorStyles.boldLabel);
  67. // Warn the user about the limitations of the quadtree implementation
  68. if (broadphaseSettings.managedReferenceFullTypename.ToLower().Contains("quadtree"))
  69. EditorGUILayout.HelpBox("Quadtree broadphase is experimental and is currently not recommended. Only supports dungeons using the default up vector (Positive Y)", MessageType.Warning);
  70. EditorGUI.indentLevel++;
  71. EditorGUILayout.PropertyField(broadphaseSettings, Label.BroadphaseSettings);
  72. EditorGUI.indentLevel--;
  73. if (enableTilePooling.boolValue)
  74. EditorGUILayout.HelpBox("Tile pooling requires special attention to how your tiles are structured. Please consult the documentation for more information", MessageType.Warning);
  75. EditorGUILayout.PropertyField(enableTilePooling, Label.TilePooling);
  76. }
  77. EditorGUILayout.EndVertical();
  78. }
  79. private void DrawBoundsProperties()
  80. {
  81. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  82. {
  83. EditorGUILayout.LabelField("Tile Bounds", EditorStyles.boldLabel);
  84. EditorGUILayout.PropertyField(ignoreSpriteBounds, Label.IgnoreSpriteBounds);
  85. EditorGUILayout.PropertyField(recalculateTileBoundsOnSave, Label.RecalculateTileBoundsOnSave);
  86. EditorGUILayout.Space();
  87. var rect = EditorGUILayout.GetControlRect();
  88. var buttonRect = rect;
  89. var dropdownRect = new Rect(buttonRect.xMax - 20, buttonRect.y, 20, buttonRect.height);
  90. buttonRect.width -= 20;
  91. if (GUI.Button(buttonRect, "Calculate Missing Tile Bounds"))
  92. AskUserToCacheBounds(false);
  93. if (EditorGUI.DropdownButton(dropdownRect, GUIContent.none, FocusType.Passive))
  94. {
  95. var menu = new GenericMenu();
  96. menu.AddItem(new GUIContent("Clear and Recalculate All Tile Bounds"), false, () => AskUserToCacheBounds(true));
  97. menu.DropDown(dropdownRect);
  98. }
  99. }
  100. EditorGUILayout.EndVertical();
  101. }
  102. private static void AskUserToCacheBounds(bool clearExisting)
  103. {
  104. string message = clearExisting
  105. ? "This will clear and recalculate bounds for all Tile prefabs in your project that belong to a TileSet. This may take some time. Continue?"
  106. : "This will calculate missing bounds for Tile prefabs in your project that belong to a TileSet. This may take some time. Continue?";
  107. if (!EditorUtility.DisplayDialog("Calculate Tile Bounds", message, "Yes", "No"))
  108. return;
  109. IEnumerable<Tile> tiles = GetAllTilePrefabs();
  110. if (!clearExisting)
  111. tiles = tiles.Where(t => !t.HasValidBounds);
  112. if (tiles.Count() == 0)
  113. EditorUtility.DisplayDialog("Calculate Tile Bounds", "No tile bounds were missing", "OK");
  114. else
  115. {
  116. int cachedBoundsCount = CacheTileBounds(tiles, clearExisting);
  117. EditorUtility.DisplayDialog("Calculate Tile Bounds", $"Recalculated bounds for {cachedBoundsCount} tiles", "OK");
  118. }
  119. }
  120. private static HashSet<Tile> GetAllTilePrefabs()
  121. {
  122. var tiles = new HashSet<Tile>();
  123. // Find all the TileSets in the project
  124. // This is much faster than loading all prefabs in the project and checking if they have a Tile component
  125. var tileSetGuids = AssetDatabase.FindAssets("t:TileSet");
  126. foreach (var guid in tileSetGuids)
  127. {
  128. string path = AssetDatabase.GUIDToAssetPath(guid);
  129. var tileSet = AssetDatabase.LoadAssetAtPath<TileSet>(path);
  130. if(tileSet != null)
  131. tiles.UnionWith(tileSet.TileWeights.Weights.Select(w => w.Value.GetComponent<Tile>()));
  132. }
  133. return tiles;
  134. }
  135. private static int CacheTileBounds(IEnumerable<Tile> tiles, bool force)
  136. {
  137. int updatedBoundsCount = 0;
  138. foreach (var tile in tiles)
  139. {
  140. bool boundsUpdated = tile.RecalculateBounds();
  141. if (boundsUpdated)
  142. {
  143. updatedBoundsCount++;
  144. EditorUtility.SetDirty(tile);
  145. }
  146. }
  147. return updatedBoundsCount;
  148. }
  149. }
  150. }