SpatialHash.cs 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. namespace MonoGame.Extended.Collisions;
  5. public class SpatialHash: ISpaceAlgorithm
  6. {
  7. private readonly Dictionary<int, List<ICollisionActor>> _dictionary = new();
  8. private readonly List<ICollisionActor> _actors = new();
  9. private readonly SizeF _size;
  10. public SpatialHash(SizeF size)
  11. {
  12. _size = size;
  13. }
  14. public void Insert(ICollisionActor actor)
  15. {
  16. InsertToHash(actor);
  17. _actors.Add(actor);
  18. }
  19. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  20. private void InsertToHash(ICollisionActor actor)
  21. {
  22. var rect = actor.Bounds.BoundingRectangle;
  23. for (var x = rect.Left; x < rect.Right; x+=_size.Width)
  24. for (var y = rect.Top; y < rect.Bottom; y+=_size.Height)
  25. AddToCell(x, y, actor);
  26. }
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. private void AddToCell(float x, float y, ICollisionActor actor)
  29. {
  30. var index = GetIndex(x, y);
  31. if (_dictionary.TryGetValue(index, out var actors))
  32. actors.Add(actor);
  33. else
  34. _dictionary[index] = new() { actor };
  35. }
  36. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  37. private int GetIndex(float x, float y)
  38. {
  39. return (int)(x / _size.Width) << 16 + (int)(y / _size.Height);
  40. }
  41. public bool Remove(ICollisionActor actor)
  42. {
  43. foreach (var actors in _dictionary.Values)
  44. actors.Remove(actor);
  45. return _actors.Remove(actor);
  46. }
  47. public IEnumerable<ICollisionActor> Query(RectangleF boundsBoundingRectangle)
  48. {
  49. var results = new HashSet<ICollisionActor>();
  50. var bounds = boundsBoundingRectangle.BoundingRectangle;
  51. for (var x = boundsBoundingRectangle.Left; x < boundsBoundingRectangle.Right; x+=_size.Width)
  52. for (var y = boundsBoundingRectangle.Top; y < boundsBoundingRectangle.Bottom; y+=_size.Height)
  53. if (_dictionary.TryGetValue(GetIndex(x, y), out var actors))
  54. foreach (var actor in actors)
  55. if (bounds.Intersects(actor.Bounds))
  56. results.Add(actor);
  57. return results;
  58. }
  59. public List<ICollisionActor>.Enumerator GetEnumerator() => _actors.GetEnumerator();
  60. public void Reset()
  61. {
  62. _dictionary.Clear();
  63. foreach (var actor in _actors)
  64. InsertToHash(actor);
  65. }
  66. }