mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-15 15:09:29 +00:00
Add spatialHash algo for collision spacing
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user