diff options
Diffstat (limited to 'Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests')
7 files changed, 967 insertions, 0 deletions
diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs new file mode 100644 index 0000000..39ac707 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/CollisionComponentTests.cs @@ -0,0 +1,416 @@ +using System; +using Microsoft.Xna.Framework; +using Xunit; + +namespace MonoGame.Extended.Collisions.Tests +{ + using MonoGame.Extended.Collisions.Layers; + + /// <summary> + /// Test collision of actors with various shapes. + /// </summary> + /// <remarks> + /// Uses the fact that <see cref="BasicActor"/> moves itself away from + /// <see cref="BasicWall"/> on collision. + /// </remarks> + public class CollisionComponentTests + { + private readonly CollisionComponent _collisionComponent; + private readonly GameTime _gameTime = new GameTime(TimeSpan.Zero, TimeSpan.FromMilliseconds(16)); + + public CollisionComponentTests() + { + _collisionComponent = new CollisionComponent(new RectangleF(Point2.Zero, new Point2(10, 10))); + } + + #region Circle Circle + + [Fact] + public void PenetrationVectorSameCircleTest() + { + Point2 pos1 = Point2.Zero; + Point2 pos2 = Point2.Zero; + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new CircleF(pos2, 2.0f); + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.Y - -4f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorSlightlyOverlappingCircleTest() + { + Point2 pos1 = new Point2(0, 1.5f); + Point2 pos2 = Point2.Zero; + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new CircleF(pos2, 2.0f); + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + // Actor should have moved up because the distance is shorter. + Assert.True(actor1.Position.Y > actor2.Position.Y); + // The circle centers should be about 4 units away after moving + Assert.True(Math.Abs(actor1.Position.Y - 4.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorSlightlyOverlappingOffAxisTest() + { + Point2 pos1 = new Point2(2, 2.5f); + Point2 pos2 = new Point2(2, 1); + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new CircleF(pos2, 2.0f); + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + // Actor should have moved up because the distance is shorter. + Assert.True(actor1.Position.Y > actor2.Position.Y); + // The circle centers should be about 4 units away after moving + Assert.True(Math.Abs(actor1.Position.Y - 5.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.X - 2.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationZeroRadiusCircleCircleTest() + { + Point2 pos1 = new Point2(0, 1.5f); + Point2 pos2 = Point2.Zero; + + IShapeF shape1 = new CircleF(pos1, 0); + IShapeF shape2 = new CircleF(pos2, 2.0f); + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + // Actor should have moved up because the distance is shorter. + Assert.True(actor1.Position.Y > actor2.Position.Y); + // The circle centers should be about 4 units away after moving + // Assert.True(Math.Abs(actor1.Position.Y - 2.0f) < float.Epsilon); + } + + #endregion + + #region Circle Rectangle + + [Fact] + public void PenetrationVectorCircleRectangleTest() + { + Point2 pos1 = new Point2(0, 1); + Point2 pos2 = new Point2(-2, -1); + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 0.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - 3.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorCircleContainedInRectangleTest() + { + Point2 pos1 = new Point2(0, 0); + Point2 pos2 = new Point2(-2, -1); + + IShapeF shape1 = new CircleF(pos1, 1.0f); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 0.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - -2.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorCircleOffAxisRectangleTest() + { + Point2 pos1 = new Point2(2, 1); + Point2 pos2 = new Point2(-2, -1); + + IShapeF shape1 = new CircleF(pos1, 2.0f); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 2.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - 3.0f) < float.Epsilon); + } + + #endregion + + #region Rectangle Rectangle + + [Fact] + public void PenetrationVectorRectangleRectangleTest() + { + Point2 pos1 = new Point2(0, 0); + Point2 pos2 = new Point2(-2, -1); + + IShapeF shape1 = new RectangleF(pos1, new Size2(4, 2)); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 0.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - 1.0f) < float.Epsilon); + } + + [Fact] + public void PenetrationVectorRectangleRectangleOffAxisTest() + { + Point2 pos1 = new Point2(4, 2); + Point2 pos2 = new Point2(3, 1); + + IShapeF shape1 = new RectangleF(pos1, new Size2(4, 2)); + IShapeF shape2 = new RectangleF(pos2, new Size2(4, 2)); + + + var actor1 = new BasicActor() + { + Position = pos1, + Bounds = shape1 + }; + var actor2 = new BasicWall() + { + Position = pos2, + Bounds = shape2 + }; + + Assert.True(shape1.Intersects(shape2)); + _collisionComponent.Insert(actor1); + _collisionComponent.Insert(actor2); + _collisionComponent.Update(_gameTime); + Assert.True(Math.Abs(actor1.Position.X - 4.0f) < float.Epsilon); + Assert.True(Math.Abs(actor1.Position.Y - 3.0f) < float.Epsilon); + } + + #endregion + + public class Behaviours : CollisionComponentTests + { + [Fact] + public void Actors_is_colliding() + { + var staticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var anotherStaticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var staticActor = new CollisionIndicatingActor(staticBounds); + var anotherStaticActor = new CollisionIndicatingActor(anotherStaticBounds); + _collisionComponent.Insert(staticActor); + _collisionComponent.Insert(anotherStaticActor); + + _collisionComponent.Update(_gameTime); + + Assert.True(staticActor.IsColliding); + Assert.True(anotherStaticActor.IsColliding); + } + + [Fact] + public void Actors_is_not_colliding_when_dynamic_actor_is_moved_out_of_collision_bounds() + { + var staticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var dynamicBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var staticActor = new CollisionIndicatingActor(staticBounds); + var dynamicActor = new CollisionIndicatingActor(dynamicBounds); + _collisionComponent.Insert(staticActor); + _collisionComponent.Insert(dynamicActor); + dynamicActor.MoveTo(new Point2(2, 2)); + + _collisionComponent.Update(_gameTime); + + Assert.False(staticActor.IsColliding); + Assert.False(dynamicActor.IsColliding); + } + + [Fact] + public void Actors_is_colliding_when_dynamic_actor_is_moved_after_update() + { + var staticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var staticActor = new CollisionIndicatingActor(staticBounds); + _collisionComponent.Insert(staticActor); + for (int i = 0; i < QuadTree.QuadTree.DefaultMaxObjectsPerNode; i++) + { + var fillerBounds = new RectangleF(new Point2(0, 2), new Size2(.1f, .1f)); + var fillerActor = new CollisionIndicatingActor(fillerBounds); + _collisionComponent.Insert(fillerActor); + } + + var dynamicBounds = new RectangleF(new Point2(2, 2), new Size2(1, 1)); + var dynamicActor = new CollisionIndicatingActor(dynamicBounds); + _collisionComponent.Insert(dynamicActor); + + _collisionComponent.Update(_gameTime); + Assert.False(staticActor.IsColliding); + Assert.False(dynamicActor.IsColliding); + + dynamicActor.MoveTo(new Point2(0, 0)); + + _collisionComponent.Update(_gameTime); + Assert.True(dynamicActor.IsColliding); + Assert.True(staticActor.IsColliding); + } + + [Fact] + public void Actors_is_colliding_when_dynamic_actor_is_moved_into_collision_bounds() + { + var staticBounds = new RectangleF(new Point2(0, 0), new Size2(1, 1)); + var dynamicBounds = new RectangleF(new Point2(2, 2), new Size2(1, 1)); + var staticActor = new CollisionIndicatingActor(staticBounds); + var dynamicActor = new CollisionIndicatingActor(dynamicBounds); + _collisionComponent.Insert(staticActor); + _collisionComponent.Insert(dynamicActor); + dynamicActor.MoveTo(new Point2(0, 0)); + + _collisionComponent.Update(_gameTime); + + Assert.True(staticActor.IsColliding); + Assert.True(dynamicActor.IsColliding); + } + + [Fact] + public void InsertActor_ThrowsUndefinedLayerException_IfThereIsNoLayerDefined() + { + var sut = new CollisionComponent(); + + var act = () => sut.Insert(new CollisionIndicatingActor(RectangleF.Empty)); + + Assert.Throws<UndefinedLayerException>(act); + } + + private class CollisionIndicatingActor : ICollisionActor + { + private RectangleF _bounds; + + public CollisionIndicatingActor(RectangleF bounds) + { + _bounds = bounds; + } + + public IShapeF Bounds => _bounds; + + public void OnCollision(CollisionEventArgs collisionInfo) + { + IsColliding = true; + } + + public bool IsColliding { get; private set; } + + public void MoveTo(Point2 position) + { + _bounds = new RectangleF(position, _bounds.Size); + } + } + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicActor.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicActor.cs new file mode 100644 index 0000000..fe56fdc --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicActor.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Collisions.Tests +{ + public class BasicActor : ICollisionActor + { + public Vector2 Position { get; set; } + public IShapeF Bounds { get; set; } + public Vector2 Velocity { get; set; } + + public BasicActor() + { + Bounds = new RectangleF(0f, 0f, 1f, 1f); + } + public void OnCollision(CollisionEventArgs collisionInfo) + { + Bounds.Position -= collisionInfo.PenetrationVector; + Position -= collisionInfo.PenetrationVector; + + if (collisionInfo.Other is BasicActor) + { + CollisionCount++; + } + else + { + Console.WriteLine(collisionInfo.Other.GetType().Name); + } + } + + public int CollisionCount { get; set; } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicWall.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicWall.cs new file mode 100644 index 0000000..3492735 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/Implementation/BasicWall.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended.Collisions.Tests +{ + public class BasicWall : ICollisionActor + { + public Vector2 Position { get; set; } + public IShapeF Bounds { get; set; } + public Vector2 Velocity { get; set; } + + public BasicWall() + { + Bounds = new RectangleF(0f, 0f, 1f, 1f); + } + public void OnCollision(CollisionEventArgs collisionInfo) + { + } + } +}
\ No newline at end of file diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj new file mode 100644 index 0000000..c62c1cd --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/MonoGame.Extended.Collisions.Tests.csproj @@ -0,0 +1,7 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <ItemGroup> + <ProjectReference Include="..\..\source\MonoGame.Extended.Collisions\MonoGame.Extended.Collisions.csproj" /> + </ItemGroup> + +</Project> diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs new file mode 100644 index 0000000..1013288 --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/QuadTreeTests.cs @@ -0,0 +1,446 @@ +using System.Collections.Generic; +using MonoGame.Extended.Collisions.QuadTree; +using Xunit; + +namespace MonoGame.Extended.Collisions.Tests +{ + public class QuadTreeTests + { + private QuadTree.QuadTree MakeTree() + { + // Bounds set to ensure actors will fit inside the tree with default bounds. + var bounds = _quadTreeArea; + var tree = new QuadTree.QuadTree(bounds); + + return tree; + } + + private RectangleF _quadTreeArea = new RectangleF(-10f, -15, 20.0f, 30.0f); + + [Fact] + public void ConstructorTest() + { + var bounds = new RectangleF(-10f, -15, 20.0f, 30.0f); + var tree = new QuadTree.QuadTree(bounds); + + Assert.Equal(bounds, tree.NodeBounds); + Assert.True(tree.IsLeaf); + } + + [Fact] + public void NumTargetsEmptyTest() + { + var tree = MakeTree(); + + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void NumTargetsOneTest() + { + var tree = MakeTree(); + var actor = new BasicActor(); + + tree.Insert(new QuadtreeData(actor)); + + Assert.Equal(1, tree.NumTargets()); + } + + + [Fact] + public void NumTargetsMultipleTest() + { + var tree = MakeTree(); + for (int i = 0; i < 5; i++) + { + tree.Insert(new QuadtreeData(new BasicActor())); + } + + Assert.Equal(5, tree.NumTargets()); + } + + [Fact] + public void NumTargetsManyTest() + { + var tree = MakeTree(); + for (int i = 0; i < 1000; i++) + { + tree.Insert(new QuadtreeData(new BasicActor())); + Assert.Equal(i + 1, tree.NumTargets()); + } + + Assert.Equal(1000, tree.NumTargets()); + } + + [Fact] + public void InsertOneTest() + { + var tree = MakeTree(); + var actor = new BasicActor(); + + tree.Insert(new QuadtreeData(actor)); + + Assert.Equal(1, tree.NumTargets()); + } + + [Fact] + public void InsertOneOverlappingQuadrantsTest() + { + var tree = MakeTree(); + var actor = new BasicActor + { + Bounds = new RectangleF(-2.5f, -2.5f, 5f, 5f) + }; + + tree.Insert(new QuadtreeData(actor)); + + Assert.Equal(1, tree.NumTargets()); + } + + [Fact] + public void InsertMultipleTest() + { + var tree = MakeTree(); + + for (int i = 0; i < 10; i++) + { + tree.Insert(new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + })); + } + + Assert.Equal(10, tree.NumTargets()); + } + + [Fact] + public void InsertManyTest() + { + var tree = MakeTree(); + + for (int i = 0; i < 1000; i++) + { + tree.Insert(new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + })); + } + + Assert.Equal(1000, tree.NumTargets()); + } + + [Fact] + public void InsertMultipleOverlappingQuadrantsTest() + { + var tree = MakeTree(); + + for (int i = 0; i < 10; i++) + { + var actor = new BasicActor() + { + Bounds = new RectangleF(-10f, -15, 20.0f, 30.0f) + }; + tree.Insert(new QuadtreeData(actor)); + } + + Assert.Equal(10, tree.NumTargets()); + } + + [Fact] + public void RemoveToEmptyTest() + { + var actor = new BasicActor() + { + Bounds = new RectangleF(-5f, -7f, 10.0f, 15.0f) + }; + var data = new QuadtreeData(actor); + + var tree = MakeTree(); + tree.Insert(data); + + tree.Remove(data); + + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void RemoveTwoTest() + { + var tree = MakeTree(); + var inserted = new List<QuadtreeData>(); + var numTargets = 2; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + }); + tree.Insert(data); + inserted.Add(data); + } + + + var inTree = numTargets; + Assert.Equal(inTree, tree.NumTargets()); + + foreach (var data in inserted) + { + tree.Remove(data); + Assert.Equal(--inTree, tree.NumTargets()); + } + } + + [Fact] + public void RemoveThreeTest() + { + var tree = MakeTree(); + var inserted = new List<QuadtreeData>(); + var numTargets = 3; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + }); + tree.Insert(data); + inserted.Add(data); + } + + + var inTree = numTargets; + Assert.Equal(inTree, tree.NumTargets()); + + foreach (var data in inserted) + { + tree.Remove(data); + Assert.Equal(--inTree, tree.NumTargets()); + } + } + + [Fact] + public void RemoveManyTest() + { + var tree = MakeTree(); + var inserted = new List<QuadtreeData>(); + var numTargets = 1000; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor() + { + Bounds = new RectangleF(0, 0, 1, 1) + }); + tree.Insert(data); + inserted.Add(data); + } + + + var inTree = numTargets; + Assert.Equal(inTree, tree.NumTargets()); + + foreach (var data in inserted) + { + data.RemoveFromAllParents(); + Assert.Equal(--inTree, tree.NumTargets()); + } + } + + + [Fact] + public void ShakeWhenEmptyTest() + { + var tree = MakeTree(); + tree.Shake(); + + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void ShakeAfterSplittingWhenEmptyTest() + { + var tree = MakeTree(); + + tree.Split(); + tree.Shake(); + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void ShakeAfterSplittingNotEmptyTest() + { + var tree = MakeTree(); + + tree.Split(); + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + tree.Shake(); + Assert.Equal(1, tree.NumTargets()); + } + + [Fact] + public void ShakeWhenContainingOneTest() + { + var tree = MakeTree(); + var numTargets = 1; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + tree.Shake(); + Assert.Equal(numTargets, tree.NumTargets()); + } + + [Fact] + public void ShakeWhenContainingTwoTest() + { + var tree = MakeTree(); + var numTargets = 2; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + tree.Shake(); + Assert.Equal(numTargets, tree.NumTargets()); + } + + [Fact] + public void ShakeWhenContainingThreeTest() + { + var tree = MakeTree(); + var numTargets = 3; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + tree.Shake(); + Assert.Equal(numTargets, tree.NumTargets()); + } + + [Fact] + public void ShakeWhenContainingManyTest() + { + var tree = MakeTree(); + var numTargets = QuadTree.QuadTree.DefaultMaxObjectsPerNode + 1; + + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + tree.Shake(); + Assert.Equal(numTargets, tree.NumTargets()); + } + + [Fact] + public void QueryWhenEmptyTest() + { + var tree = MakeTree(); + + var query = tree.Query(ref _quadTreeArea); + + Assert.Empty(query); + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void QueryNotOverlappingTest() + { + var tree = MakeTree(); + + var area = new RectangleF(100f, 100f, 1f, 1f); + var query = tree.Query(ref area); + + Assert.Empty(query); + Assert.Equal(0, tree.NumTargets()); + } + + [Fact] + public void QueryLeafNodeNotEmptyTest() + { + var tree = MakeTree(); + var actor = new BasicActor(); + tree.Insert(new QuadtreeData(actor)); + + var query = tree.Query(ref _quadTreeArea); + Assert.Single(query); + Assert.Equal(tree.NumTargets(), query.Count); + } + + [Fact] + public void QueryLeafNodeNoOverlapTest() + { + var tree = MakeTree(); + var actor = new BasicActor(); + tree.Insert(new QuadtreeData(actor)); + + var area = new RectangleF(100f, 100f, 1f, 1f); + var query = tree.Query(ref area); + Assert.Empty(query); + } + + [Fact] + public void QueryLeafNodeMultipleTest() + { + var tree = MakeTree(); + var numTargets = QuadTree.QuadTree.DefaultMaxObjectsPerNode; + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + + var query = tree.Query(ref _quadTreeArea); + Assert.Equal(numTargets, query.Count); + Assert.Equal(tree.NumTargets(), query.Count); + } + + [Fact] + public void QueryNonLeafManyTest() + { + var tree = MakeTree(); + var numTargets = 2*QuadTree.QuadTree.DefaultMaxObjectsPerNode; + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + + var query = tree.Query(ref _quadTreeArea); + Assert.Equal(numTargets, query.Count); + Assert.Equal(tree.NumTargets(), query.Count); + } + + [Fact] + public void QueryTwiceConsecutiveTest() + { + var tree = MakeTree(); + var numTargets = 2 * QuadTree.QuadTree.DefaultMaxObjectsPerNode; + for (int i = 0; i < numTargets; i++) + { + var data = new QuadtreeData(new BasicActor()); + tree.Insert(data); + } + + + var query1 = tree.Query(ref _quadTreeArea); + var query2 = tree.Query(ref _quadTreeArea); + Assert.Equal(numTargets, query1.Count); + Assert.Equal(tree.NumTargets(), query1.Count); + Assert.Equal(query1.Count, query2.Count); + } + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs new file mode 100644 index 0000000..7c09a9e --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/SpatialHashTests.cs @@ -0,0 +1,39 @@ +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() + { + Bounds = RECT, + }); + var collisions = hash.Query(RECT); + Assert.Equal(1, collisions.Count()); + } + + [Fact] + public void CollisionTwoTest() + { + var hash = generateSpatialHash(); + hash.Insert(new BasicActor + { + Bounds = RECT, + }); + hash.Insert(new BasicActor + { + Bounds = RECT, + }); + var collisions = hash.Query(RECT); + Assert.Equal(2, collisions.Count()); + } +} diff --git a/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/packages.config b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/packages.config new file mode 100644 index 0000000..fa95cfc --- /dev/null +++ b/Plugins/MonoGame.Extended/tests/MonoGame.Extended.Collisions.Tests/packages.config @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="MonoGame.Framework.WindowsDX" version="3.6.0.1625" targetFramework="net452" /> + <package id="NSubstitute" version="1.10.0.0" targetFramework="net452" /> + <package id="NUnit" version="2.6.4" targetFramework="net452" /> +</packages>
\ No newline at end of file |