diff --git a/Bonk/BroadPhase/SpatialHash.cs b/Bonk/BroadPhase/SpatialHash.cs
index 8f54856..cdd96bd 100644
--- a/Bonk/BroadPhase/SpatialHash.cs
+++ b/Bonk/BroadPhase/SpatialHash.cs
@@ -73,7 +73,38 @@ namespace MoonTools.Core.Bonk
foreach (var t in hashDictionary[key])
{
var (otherShape, otherTransform) = IDLookup[t];
- if (!id.Equals(t) && AABB.TestOverlap(shape.TransformedAABB(transform2D), otherShape.TransformedAABB(otherTransform)))
+ if (!id.Equals(t) && AABB.TestOverlap(box, otherShape.TransformedAABB(otherTransform)))
+ {
+ yield return (t, otherShape, otherTransform);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ ///
+ /// Retrieves objects based on a pre-transformed AABB.
+ ///
+ /// A transformed AABB.
+ ///
+ public IEnumerable<(T, IHasAABB2D, Transform2D)> Retrieve(AABB aabb)
+ {
+ var minHash = Hash(aabb.Min);
+ var maxHash = Hash(aabb.Max);
+
+ for (var i = minHash.Item1; i <= maxHash.Item1; i++)
+ {
+ for (var j = minHash.Item2; j <= maxHash.Item2; j++)
+ {
+ var key = MakeLong(i, j);
+ if (hashDictionary.ContainsKey(key))
+ {
+ foreach (var t in hashDictionary[key])
+ {
+ var (otherShape, otherTransform) = IDLookup[t];
+ if (AABB.TestOverlap(aabb, otherShape.TransformedAABB(otherTransform)))
{
yield return (t, otherShape, otherTransform);
}
diff --git a/Bonk/MultiShape.cs b/Bonk/MultiShape.cs
index e9af874..eb34cda 100644
--- a/Bonk/MultiShape.cs
+++ b/Bonk/MultiShape.cs
@@ -30,6 +30,16 @@ namespace MoonTools.Core.Bonk
}
}
+ public bool IsSingleShape() where T : struct, IShape2D
+ {
+ return ShapeTransformPairs.Length == 1 && ShapeTransformPairs[0].Item1 is T;
+ }
+
+ public (T, Transform2D) ShapeTransformPair() where T : struct, IShape2D
+ {
+ return ((T, Transform2D))ShapeTransformPairs[0];
+ }
+
private static AABB AABBFromShapes(IEnumerable<(IShape2D, Transform2D)> shapeTransforms)
{
var minX = float.MaxValue;
diff --git a/Bonk/SweepTest/SweepTest.cs b/Bonk/SweepTest/SweepTest.cs
index 5001188..65ea470 100644
--- a/Bonk/SweepTest/SweepTest.cs
+++ b/Bonk/SweepTest/SweepTest.cs
@@ -7,7 +7,7 @@ namespace MoonTools.Core.Bonk
public static class SweepTest
{
///
- /// Performs a sweep test on rectangles.
+ /// Performs a sweep test on rectangles. Returns the position 1 pixel before overlap occurs.
///
///
/// A spatial hash.
@@ -27,67 +27,88 @@ namespace MoonTools.Core.Bonk
foreach (var (id, shape, shapeTransform) in spatialHash.Retrieve(sweepBox))
{
- if (shape is Rectangle otherRectangle)
+ Rectangle otherRectangle;
+ Transform2D otherTransform;
+ AABB otherTransformedAABB;
+ if (shape is Rectangle)
{
- var otherTransformedAABB = otherRectangle.TransformedAABB(shapeTransform);
- float xInvEntry, yInvEntry;
+ otherRectangle = (Rectangle)shape;
+ otherTransformedAABB = shape.TransformedAABB(shapeTransform);
+ otherTransform = shapeTransform;
+ }
+ else if (shape is MultiShape multiShape && multiShape.IsSingleShape())
+ {
+ Transform2D rectangleOffset;
+ (otherRectangle, rectangleOffset) = multiShape.ShapeTransformPair();
+ otherTransform = shapeTransform.Compose(rectangleOffset);
+ otherTransformedAABB = shape.TransformedAABB(otherTransform);
+ }
+ else
+ {
+ continue;
+ }
- if (ray.X > 0)
- {
- xInvEntry = shapeTransform.Position.X - (transform.Position.X + transformedAABB.Width);
- }
- else
- {
- xInvEntry = (shapeTransform.Position.X + otherTransformedAABB.Width) - transform.Position.X;
- }
+ float xInvEntry, yInvEntry;
- if (ray.Y > 0)
- {
- yInvEntry = shapeTransform.Position.Y - (transform.Position.Y + transformedAABB.Height);
- }
- else
- {
- yInvEntry = (shapeTransform.Position.Y + otherTransformedAABB.Height) - shapeTransform.Position.Y;
- }
+ if (ray.X > 0)
+ {
+ xInvEntry = otherTransformedAABB.Left - (transformedAABB.Right);
+ }
+ else
+ {
+ xInvEntry = (otherTransformedAABB.Right) - transformedAABB.Left;
+ }
- float xEntry, yEntry;
+ if (ray.Y > 0)
+ {
+ yInvEntry = otherTransformedAABB.Top - (transformedAABB.Bottom);
+ }
+ else
+ {
+ yInvEntry = (otherTransformedAABB.Bottom) - transformedAABB.Top;
+ }
- if (ray.X == 0)
- {
- xEntry = float.MinValue;
- }
- else
- {
- xEntry = xInvEntry / ray.X;
- }
+ float xEntry, yEntry;
- if (ray.Y == 0)
- {
- yEntry = float.MinValue;
- }
- else
- {
- yEntry = yInvEntry / ray.Y;
- }
+ if (ray.X == 0)
+ {
+ xEntry = float.MinValue;
+ }
+ else
+ {
+ xEntry = xInvEntry / ray.X;
+ }
- var entryTime = Math.Max(xEntry, yEntry);
+ if (ray.Y == 0)
+ {
+ yEntry = float.MinValue;
+ }
+ else
+ {
+ yEntry = yInvEntry / ray.Y;
+ }
- if (entryTime > 0 && entryTime < 1)
+ var entryTime = Math.Max(xEntry, yEntry);
+
+ if (entryTime >= 0 && entryTime <= 1)
+ {
+ if (entryTime < shortestDistance)
{
- if (entryTime < shortestDistance)
- {
- shortestDistance = entryTime;
- nearestID = id;
- nearestRectangle = rectangle;
- nearestTransform = shapeTransform;
- }
+ shortestDistance = entryTime;
+ nearestID = id;
+ nearestRectangle = otherRectangle;
+ nearestTransform = shapeTransform;
}
}
+
}
if (nearestRectangle.HasValue)
{
- return new SweepResult(true, ray * shortestDistance, nearestID, nearestRectangle.Value, nearestTransform.Value);
+ var overlapPosition = ray * shortestDistance;
+ var correctionX = ray.X > 0 ? -1 : 1;
+ var correctionY = ray.Y > 0 ? -1 : 1;
+ return new SweepResult(true, new Position2D((int)overlapPosition.X + correctionX, (int)overlapPosition.Y + correctionY), nearestID, nearestRectangle.Value, nearestTransform.Value);
}
else
{
diff --git a/Test/SweepTestTest.cs b/Test/SweepTestTest.cs
index cd7b1d4..cf07de6 100644
--- a/Test/SweepTestTest.cs
+++ b/Test/SweepTestTest.cs
@@ -20,15 +20,23 @@ namespace Tests
var farthestRectangle = new Rectangle(4, 4);
var farthestTransform = new Transform2D(new Position2D(12, 0));
+ var downRectangle = new Rectangle(12, 4);
+ var downTransform = new Transform2D(new Position2D(-6, 20));
+
var spatialHash = new SpatialHash(16);
spatialHash.Insert(1, otherRectangle, otherTransform);
spatialHash.Insert(2, farthestRectangle, farthestTransform);
+ spatialHash.Insert(3, downRectangle, downTransform);
SweepTest.Rectangle(spatialHash, rectangle, transform, new Vector2(12, 0)).Should().Be(
new SweepResult(true, new Vector2(8, 0), 1, otherRectangle, otherTransform)
);
SweepTest.Rectangle(spatialHash, rectangle, transform, new Vector2(-12, 0)).Hit.Should().BeFalse();
+
+ SweepTest.Rectangle(spatialHash, rectangle, transform, new Vector2(0, 20)).Should().Be(
+ new SweepResult(true, new Vector2(0, 16), 3, downRectangle, downTransform)
+ );
}
}
}