RuntimeAnalyzer.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. using DunGen.Graph;
  2. using System.Text;
  3. using UnityEngine;
  4. using Stopwatch = System.Diagnostics.Stopwatch;
  5. namespace DunGen.Analysis
  6. {
  7. public delegate void RuntimeAnalyzerDelegate(RuntimeAnalyzer analyzer);
  8. public delegate void AnalysisUpdatedDelegate(RuntimeAnalyzer analyzer, GenerationAnalysis analysis, GenerationStats generationStats, int currentIteration, int totalIterations);
  9. [AddComponentMenu("DunGen/Analysis/Runtime Analyzer")]
  10. public sealed class RuntimeAnalyzer : MonoBehaviour
  11. {
  12. #region Nested Types
  13. public enum SeedMode
  14. {
  15. Random,
  16. Incremental,
  17. Fixed,
  18. }
  19. #endregion
  20. public static event RuntimeAnalyzerDelegate AnalysisStarted;
  21. public static event RuntimeAnalyzerDelegate AnalysisComplete;
  22. public static event AnalysisUpdatedDelegate AnalysisUpdated;
  23. public DungeonFlow DungeonFlow;
  24. public int Iterations = 100;
  25. public int MaxFailedAttempts = 20;
  26. public bool RunOnStart = true;
  27. public float MaximumAnalysisTime = 0;
  28. public SeedMode SeedGenerationMode = SeedMode.Random;
  29. public int Seed = 0;
  30. public bool ClearDungeonOnCompletion = true;
  31. public bool AllowTilePooling = false;
  32. private DungeonGenerator generator = new DungeonGenerator();
  33. private GenerationAnalysis analysis;
  34. private StringBuilder infoText = new StringBuilder();
  35. private bool finishedEarly;
  36. private bool prevShouldRandomizeSeed;
  37. private int targetIterations;
  38. private int currentIterations { get { return targetIterations - remainingIterations; } }
  39. private int remainingIterations;
  40. private Stopwatch analysisTime;
  41. private bool generateNextFrame;
  42. private int currentSeed;
  43. private RandomStream randomStream;
  44. private void Start()
  45. {
  46. if (RunOnStart)
  47. Analyze();
  48. }
  49. public void Analyze()
  50. {
  51. bool isValid = false;
  52. if (DungeonFlow == null)
  53. Debug.LogError("No DungeonFlow assigned to analyzer");
  54. else if (Iterations <= 0)
  55. Debug.LogError("Iteration count must be greater than 0");
  56. else if (MaxFailedAttempts <= 0)
  57. Debug.LogError("Max failed attempt count must be greater than 0");
  58. else
  59. isValid = true;
  60. if (!isValid)
  61. return;
  62. AnalysisStarted?.Invoke(this);
  63. prevShouldRandomizeSeed = generator.ShouldRandomizeSeed;
  64. generator.IsAnalysis = true;
  65. generator.DungeonFlow = DungeonFlow;
  66. generator.MaxAttemptCount = MaxFailedAttempts;
  67. generator.ShouldRandomizeSeed = false;
  68. generator.AllowTilePooling = AllowTilePooling;
  69. analysis = new GenerationAnalysis(Iterations);
  70. analysisTime = Stopwatch.StartNew();
  71. remainingIterations = targetIterations = Iterations;
  72. randomStream = new RandomStream(Seed);
  73. generator.OnGenerationStatusChanged += OnGenerationStatusChanged;
  74. GenerateNext();
  75. }
  76. private void GenerateNext()
  77. {
  78. switch(SeedGenerationMode)
  79. {
  80. case SeedMode.Random:
  81. currentSeed = randomStream.Next();
  82. break;
  83. case SeedMode.Incremental:
  84. currentSeed++;
  85. break;
  86. case SeedMode.Fixed:
  87. currentSeed = Seed;
  88. break;
  89. }
  90. generator.Seed = currentSeed;
  91. generator.Generate();
  92. }
  93. private void Update()
  94. {
  95. if (MaximumAnalysisTime > 0 && analysisTime.Elapsed.TotalSeconds >= MaximumAnalysisTime)
  96. {
  97. remainingIterations = 0;
  98. finishedEarly = true;
  99. }
  100. if (generateNextFrame)
  101. {
  102. generateNextFrame = false;
  103. GenerateNext();
  104. }
  105. }
  106. private void CompleteAnalysis()
  107. {
  108. analysisTime.Stop();
  109. analysis.Analyze();
  110. if(ClearDungeonOnCompletion)
  111. UnityUtil.Destroy(generator.Root);
  112. OnAnalysisComplete();
  113. AnalysisComplete?.Invoke(this);
  114. }
  115. private void OnGenerationStatusChanged(DungeonGenerator generator, GenerationStatus status)
  116. {
  117. if (status != GenerationStatus.Complete)
  118. return;
  119. analysis.IncrementSuccessCount();
  120. analysis.Add(generator.GenerationStats);
  121. AnalysisUpdated?.Invoke(this, analysis, generator.GenerationStats, currentIterations, targetIterations);
  122. remainingIterations--;
  123. if (remainingIterations <= 0)
  124. {
  125. generator.OnGenerationStatusChanged -= OnGenerationStatusChanged;
  126. CompleteAnalysis();
  127. }
  128. else
  129. generateNextFrame = true;
  130. }
  131. private void OnAnalysisComplete()
  132. {
  133. const int textPadding = 20;
  134. void AddInfoEntry(StringBuilder stringBuilder, string title, NumberSetData data)
  135. {
  136. string spacing = new string(' ', textPadding - title.Length);
  137. stringBuilder.Append($"\n\t{title}:{spacing}\t{data}");
  138. }
  139. generator.ShouldRandomizeSeed = prevShouldRandomizeSeed;
  140. infoText.Length = 0;
  141. if (finishedEarly)
  142. infoText.AppendLine("[ Reached maximum analysis time before the target number of iterations was reached ]");
  143. infoText.AppendFormat("Iterations: {0}, Max Failed Attempts: {1}", (finishedEarly) ? analysis.IterationCount : analysis.TargetIterationCount, MaxFailedAttempts);
  144. infoText.AppendFormat("\nTotal Analysis Time: {0:0.00} seconds", analysisTime.Elapsed.TotalSeconds);
  145. //infoText.AppendFormat("\n\tOf which spent generating dungeons: {0:0.00} seconds", analysis.AnalysisTime / 1000.0f);
  146. infoText.AppendFormat("\nDungeons successfully generated: {0}% ({1} failed)", Mathf.RoundToInt(analysis.SuccessPercentage), analysis.TargetIterationCount - analysis.SuccessCount);
  147. infoText.AppendLine();
  148. infoText.AppendLine();
  149. infoText.Append("## TIME TAKEN (in milliseconds) ##");
  150. foreach (var step in GenerationAnalysis.MeasurableSteps)
  151. AddInfoEntry(infoText, step.ToString(), analysis.GetGenerationStepData(step));
  152. infoText.Append("\n\t-------------------------------------------------------");
  153. AddInfoEntry(infoText, "Total", analysis.TotalTime);
  154. infoText.AppendLine();
  155. infoText.AppendLine();
  156. infoText.AppendLine("## ROOM DATA ##");
  157. AddInfoEntry(infoText, "Main Path Rooms", analysis.MainPathRoomCount);
  158. AddInfoEntry(infoText, "Branch Path Rooms", analysis.BranchPathRoomCount);
  159. infoText.Append("\n\t-------------------");
  160. AddInfoEntry(infoText, "Total", analysis.TotalRoomCount);
  161. infoText.AppendLine();
  162. infoText.AppendLine();
  163. infoText.AppendFormat("Retry Count: {0}", analysis.TotalRetries);
  164. }
  165. private void OnGUI()
  166. {
  167. if (analysis == null || infoText == null || infoText.Length == 0)
  168. {
  169. string failedGenerationsCountText = (analysis.SuccessCount < analysis.IterationCount) ? ("\nFailed Dungeons: " + (analysis.IterationCount - analysis.SuccessCount).ToString()) : "";
  170. GUILayout.Label(string.Format("Analysing... {0} / {1} ({2:0.0}%){3}", currentIterations, targetIterations, (currentIterations / (float)targetIterations) * 100, failedGenerationsCountText));
  171. return;
  172. }
  173. GUILayout.Label(infoText.ToString());
  174. }
  175. }
  176. }