DungeonCollisionManager.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. using System.Collections.Generic;
  2. using Unity.Profiling;
  3. using UnityEngine;
  4. namespace DunGen.Collision
  5. {
  6. public class DungeonCollisionManager
  7. {
  8. private static readonly ProfilerMarker initPerfMarker = new ProfilerMarker("DungeonCollisionManager.Initialize");
  9. private static readonly ProfilerMarker preCachePerfMarker = new ProfilerMarker("DungeonCollisionManager.PreCacheCounds");
  10. private static readonly ProfilerMarker addTilePerMarker = new ProfilerMarker("DungeonCollisionManager.AddTile");
  11. private static readonly ProfilerMarker removeTilePerfMarker = new ProfilerMarker("DungeonCollisionManager.RemoveTile");
  12. private static readonly ProfilerMarker collisionBroadPhasePerfMarker = new ProfilerMarker("DungeonCollisionManager.BroadPhase");
  13. private static readonly ProfilerMarker collisionNarrowPhasePerfMarker = new ProfilerMarker("DungeonCollisionManager.NarrowPhase");
  14. public DungeonCollisionSettings Settings { get; set; }
  15. public ICollisionBroadphase Broadphase { get; private set; }
  16. private readonly List<Bounds> cachedBounds = new List<Bounds>();
  17. private readonly List<TileProxy> tiles = new List<TileProxy>();
  18. private List<Bounds> boundsToCheck = new List<Bounds>();
  19. /// <summary>
  20. /// Initializes the collision manager. This should be done at the beginning of the dungeon generation process
  21. /// </summary>
  22. /// <param name="dungeonGenerator">The dungeon generator we're initializing for</param>
  23. public virtual void Initialize(DungeonGenerator dungeonGenerator)
  24. {
  25. using(initPerfMarker.Auto())
  26. {
  27. Clear();
  28. PreCacheBounds(dungeonGenerator);
  29. InitializeBroadphase(dungeonGenerator);
  30. }
  31. }
  32. protected virtual void Clear()
  33. {
  34. tiles.Clear();
  35. cachedBounds.Clear();
  36. boundsToCheck.Clear();
  37. }
  38. protected virtual void PreCacheBounds(DungeonGenerator dungeonGenerator)
  39. {
  40. using (preCachePerfMarker.Auto())
  41. {
  42. cachedBounds.Clear();
  43. // Cache tiles from other dungeons if we need to avoid collisions with them
  44. if (Settings.AvoidCollisionsWithOtherDungeons || dungeonGenerator.AttachmentSettings != null)
  45. {
  46. foreach (var tile in UnityUtil.FindObjectsByType<Tile>())
  47. cachedBounds.Add(tile.Placement.Bounds);
  48. }
  49. // Add all additional collision bounds to the cache
  50. foreach (var bounds in Settings.AdditionalCollisionBounds)
  51. cachedBounds.Add(bounds);
  52. }
  53. }
  54. protected virtual void InitializeBroadphase(DungeonGenerator dungeonGenerator)
  55. {
  56. var broadphaseSettings = DunGenSettings.Instance.BroadphaseSettings;
  57. if (broadphaseSettings == null)
  58. {
  59. Broadphase = null;
  60. return;
  61. }
  62. Broadphase = broadphaseSettings.Create();
  63. Broadphase.Init(broadphaseSettings, dungeonGenerator);
  64. // Add all cached bounds to the quadtree
  65. foreach(var bounds in cachedBounds)
  66. Broadphase.Insert(bounds);
  67. }
  68. /// <summary>
  69. /// Adds a tile to the collision manager
  70. /// </summary>
  71. /// <param name="tile">The tile to add</param>
  72. public virtual void AddTile(TileProxy tile)
  73. {
  74. using(addTilePerMarker.Auto())
  75. {
  76. tiles.Add(tile);
  77. Broadphase?.Insert(tile.Placement.Bounds);
  78. }
  79. }
  80. /// <summary>
  81. /// Removed a tile from the collision manager
  82. /// </summary>
  83. /// <param name="tile">The tile to remove</param>
  84. public virtual void RemoveTile(TileProxy tile)
  85. {
  86. using (removeTilePerfMarker.Auto())
  87. {
  88. tiles.Remove(tile);
  89. Broadphase?.Remove(tile.Placement.Bounds);
  90. }
  91. }
  92. /// <summary>
  93. /// Checks if a tile is colliding with any other tiles in the dungeon
  94. /// </summary>
  95. /// <param name="upDirection">The up direction for the dungeon</param>
  96. /// <param name="prospectiveNewTile">The new tile we'd like to spawn</param>
  97. /// <param name="previousTile">The tile we're trying to attach to</param>
  98. /// <returns>True if any blocking collision occurs</returns>
  99. public virtual bool IsCollidingWithAnyTile(AxisDirection upDirection, TileProxy prospectiveNewTile, TileProxy previousTile)
  100. {
  101. bool isColliding = false;
  102. using (collisionBroadPhasePerfMarker.Auto())
  103. {
  104. UpdateBoundsToCheck(prospectiveNewTile, previousTile);
  105. }
  106. using (collisionNarrowPhasePerfMarker.Auto())
  107. {
  108. // Check for collisions with potentially colliding tiles
  109. for (int i = 0; i < boundsToCheck.Count; i++)
  110. {
  111. var bounds = boundsToCheck[i];
  112. bool isConnected = previousTile != null && i == 0;
  113. float maxOverlap = (isConnected) ? Settings.OverlapThreshold : -Settings.Padding;
  114. if (Settings.DisallowOverhangs && !isConnected)
  115. {
  116. if (UnityUtil.AreBoundsOverlappingOrOverhanging(prospectiveNewTile.Placement.Bounds,
  117. bounds,
  118. upDirection,
  119. maxOverlap))
  120. {
  121. isColliding = true;
  122. break;
  123. }
  124. }
  125. else
  126. {
  127. if (UnityUtil.AreBoundsOverlapping(prospectiveNewTile.Placement.Bounds, bounds, maxOverlap))
  128. {
  129. isColliding = true;
  130. break;
  131. }
  132. }
  133. }
  134. }
  135. // Process custom collision predicate if there is one
  136. if (Settings.AdditionalCollisionsPredicate != null)
  137. isColliding = Settings.AdditionalCollisionsPredicate(prospectiveNewTile.Placement.Bounds, isColliding);
  138. return isColliding;
  139. }
  140. protected virtual void UpdateBoundsToCheck(TileProxy prospectiveNewTile, TileProxy previousTile)
  141. {
  142. boundsToCheck.Clear();
  143. if (Broadphase != null)
  144. {
  145. Broadphase.Query(prospectiveNewTile.Placement.Bounds, ref boundsToCheck);
  146. // Ensure previous tile bounds is at the front of the list
  147. if (previousTile != null)
  148. {
  149. var previousBounds = previousTile.Placement.Bounds;
  150. int existingIndex = boundsToCheck.FindIndex(b => b.Equals(previousBounds));
  151. if (existingIndex != -1)
  152. {
  153. boundsToCheck.RemoveAt(existingIndex);
  154. boundsToCheck.Insert(0, previousBounds);
  155. }
  156. else
  157. boundsToCheck.Insert(0, previousBounds);
  158. }
  159. }
  160. else
  161. {
  162. // Ensure previous tile bounds is at the front of the list
  163. if (previousTile != null)
  164. boundsToCheck.Add(previousTile.Placement.Bounds);
  165. foreach (var tile in tiles)
  166. if (tile != previousTile)
  167. boundsToCheck.Add(tile.Placement.Bounds);
  168. boundsToCheck.AddRange(cachedBounds);
  169. }
  170. }
  171. }
  172. }