Add spatialHash algo for collision spacing

This commit is contained in:
Gandifil
2023-11-25 02:33:14 +03:00
parent af0658d512
commit 12a6b95c86
2 changed files with 105 additions and 0 deletions
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace MonoGame.Extended.Collisions;
public class SpatialHash: ISpaceAlgorithm
{
private readonly Dictionary<int, List<ICollisionActor>> _dictionary = new();
private readonly List<ICollisionActor> _actors = new();
private readonly Size2 _size;
public SpatialHash(Size2 size)
{
_size = size;
}
public void Insert(ICollisionActor actor)
{
InsertToHash(actor);
_actors.Add(actor);
}
private void InsertToHash(ICollisionActor actor)
{
var rect = actor.Bounds.BoundingRectangle;
for (var x = rect.Left; x < rect.Right; x+=_size.Width)
for (var y = rect.Top; y < rect.Bottom; y+=_size.Height)
AddToCell(x, y, actor);
}
private void AddToCell(float x, float y, ICollisionActor actor)
{
var index = GetIndex(x, y);
if (_dictionary.TryGetValue(index, out var actors))
actors.Add(actor);
else
_dictionary[index] = new() { actor };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetIndex(float x, float y)
{
return (int)(x / _size.Width) << 16 + (int)(y / _size.Height);
}
public bool Remove(ICollisionActor actor)
{
foreach (var actors in _dictionary.Values)
actors.Remove(actor);
return _actors.Remove(actor);
}
public IEnumerable<ICollisionActor> Query(RectangleF boundsBoundingRectangle)
{
var results = new HashSet<ICollisionActor>();
for (var x = boundsBoundingRectangle.Left; x < boundsBoundingRectangle.Right; x+=_size.Width)
for (var y = boundsBoundingRectangle.Top; y < boundsBoundingRectangle.Bottom; y+=_size.Height)
if (_dictionary.TryGetValue(GetIndex(x, y), out var actors))
foreach (var actor in actors)
results.Add(actor);
return results;
}
public List<ICollisionActor>.Enumerator GetEnumerator() => _actors.GetEnumerator();
public void Reset()
{
_dictionary.Clear();
foreach (var actor in _actors)
InsertToHash(actor);
}
}
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace MonoGame.Extended.Collisions.Tests;
public class SpatialHashTests
{
private SpatialHash generateSpatialHash() => new SpatialHash(new Size2(64, 64));
private readonly RectangleF RECT = new RectangleF(10, 10, 20, 20);
[Fact]
public void CollisionOneTrueTest()
{
var hash = generateSpatialHash();
hash.Insert(new BasicActor());
var collisions = hash.Query(RECT);
Assert.Equal(1, collisions.Count());
}
[Fact]
public void CollisionTwoTest()
{
var hash = generateSpatialHash();
hash.Insert(new BasicActor());
hash.Insert(new BasicActor());
var collisions = hash.Query(RECT);
Assert.Equal(2, collisions.Count());
}
}