diff options
Diffstat (limited to 'core/src/test/java/coffee/liz/ecs')
4 files changed, 199 insertions, 28 deletions
diff --git a/core/src/test/java/coffee/liz/ecs/DAGWorldTest.java b/core/src/test/java/coffee/liz/ecs/DAGWorldTest.java index 4825e36..7f3dc2f 100644 --- a/core/src/test/java/coffee/liz/ecs/DAGWorldTest.java +++ b/core/src/test/java/coffee/liz/ecs/DAGWorldTest.java @@ -17,7 +17,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.time.Duration; import java.util.Collection; import java.util.List; import java.util.Map; @@ -30,7 +29,7 @@ public class DAGWorldTest { @ParameterizedTest @MethodSource("queryScenarios") public void queryResolvesExpectedEntities(final Query query, final Set<String> expectedEntityKeys) { - final World<String> world = new DAGWorld<>(); + final World world = new DAGWorld(); final Entity both = world.createEntity(); both.add(new PositionComponent()); both.add(new VelocityComponent()); @@ -40,7 +39,7 @@ public class DAGWorldTest { velocityOnly.add(new VelocityComponent()); final Entity neither = world.createEntity(); - world.update("state", 0); + world.update(0); final Map<String, Entity> entities = Map.of("both", both, "positionOnly", positionOnly, "velocityOnly", velocityOnly, "neither", neither); @@ -60,16 +59,16 @@ public class DAGWorldTest { public void updateExecutesSystemsInTopologicalOrder() { final CopyOnWriteArrayList<String> executionLog = new CopyOnWriteArrayList<>(); - final DAGWorld<String> world = new DAGWorld<>(new SystemC(executionLog), new SystemA(executionLog), + final DAGWorld world = new DAGWorld(new SystemC(executionLog), new SystemA(executionLog), new SystemB(executionLog)); - world.update("state", 0); + world.update(0); assertEquals(List.of("A", "B", "C"), executionLog); } @Test public void cacheTracksComponentMutationsViaEntityEvents() { - final DAGWorld<String> world = new DAGWorld<>(); + final DAGWorld world = new DAGWorld(); final Entity subject = world.createEntity(); assertTrue(world.resolve(Query.allOf(PositionComponent.class)).isEmpty()); @@ -83,7 +82,7 @@ public class DAGWorldTest { @Test public void removedEntityNoLongerMutatesWorldCache() { - final DAGWorld<String> world = new DAGWorld<>(); + final DAGWorld world = new DAGWorld(); final Entity subject = world.createEntity(); subject.add(new PositionComponent()); @@ -100,13 +99,12 @@ public class DAGWorldTest { public void queryFindsComponentsAddedByEarlierSystemInSameTick() { final List<Set<Entity>> queryResults = new CopyOnWriteArrayList<>(); - final DAGWorld<String> world = new DAGWorld<>(new ComponentAdderSystem(), - new ComponentReaderSystem(queryResults)); + final DAGWorld world = new DAGWorld(new ComponentAdderSystem(), new ComponentReaderSystem(queryResults)); final Entity entity = world.createEntity(); entity.add(new PositionComponent()); - world.update("state", 0); + world.update(0); assertEquals(1, queryResults.size()); assertEquals(Set.of(entity), queryResults.get(0)); @@ -114,7 +112,7 @@ public class DAGWorldTest { @Test public void circularDependencyDetectionThrowsIllegalStateException() { - assertThrows(IllegalStateException.class, () -> new DAGWorld<>(new SystemCycleA(), new SystemCycleB())); + assertThrows(IllegalStateException.class, () -> new DAGWorld(new SystemCycleA(), new SystemCycleB())); } private static final class PositionComponent implements Component { @@ -124,12 +122,12 @@ public class DAGWorldTest { } @RequiredArgsConstructor - private abstract static class RecordingSystem implements System<String> { + private abstract static class RecordingSystem implements System { private final List<String> log; private final String label; @Override - public final void update(final World<String> world, final String state, final float deltaSeconds) { + public final void update(final World world, final float deltaSeconds) { log.add(label); } } @@ -140,7 +138,7 @@ public class DAGWorldTest { } @Override - public Collection<Class<? extends System<String>>> getDependencies() { + public Collection<Class<? extends System>> getDependencies() { return List.of(); } } @@ -151,7 +149,7 @@ public class DAGWorldTest { } @Override - public Collection<Class<? extends System<String>>> getDependencies() { + public Collection<Class<? extends System>> getDependencies() { return Set.of(SystemA.class); } } @@ -162,56 +160,56 @@ public class DAGWorldTest { } @Override - public Collection<Class<? extends System<String>>> getDependencies() { + public Collection<Class<? extends System>> getDependencies() { return Set.of(SystemB.class); } } - private static final class SystemCycleA implements System<String> { + private static final class SystemCycleA implements System { @Override - public Collection<Class<? extends System<String>>> getDependencies() { + public Collection<Class<? extends System>> getDependencies() { return Set.of(SystemCycleB.class); } @Override - public final void update(final World<String> world, final String state, final float deltaSeconds) { + public void update(final World world, final float deltaSeconds) { } } - private static final class SystemCycleB implements System<String> { + private static final class SystemCycleB implements System { @Override - public Collection<Class<? extends System<String>>> getDependencies() { + public Collection<Class<? extends System>> getDependencies() { return Set.of(SystemCycleA.class); } @Override - public final void update(final World<String> world, final String state, final float deltaSeconds) { + public void update(final World world, final float deltaSeconds) { } } - private static final class ComponentAdderSystem implements System<String> { + private static final class ComponentAdderSystem implements System { @Override - public Collection<Class<? extends System<String>>> getDependencies() { + public Collection<Class<? extends System>> getDependencies() { return List.of(); } @Override - public final void update(final World<String> world, final String state, final float deltaSeconds) { + public void update(final World world, final float deltaSeconds) { world.resolve(Query.allOf(PositionComponent.class)).forEach(e -> e.add(new VelocityComponent())); } } @RequiredArgsConstructor - private static final class ComponentReaderSystem implements System<String> { + private static final class ComponentReaderSystem implements System { private final List<Set<Entity>> queryResults; @Override - public Collection<Class<? extends System<String>>> getDependencies() { + public Collection<Class<? extends System>> getDependencies() { return Set.of(ComponentAdderSystem.class); } @Override - public final void update(final World<String> world, final String state, final float deltaSeconds) { + public void update(final World world, final float deltaSeconds) { queryResults.add(world.resolve(Query.allOf(VelocityComponent.class, PositionComponent.class))); } } diff --git a/core/src/test/java/coffee/liz/ecs/common/components/physics/BoundingBoxTest.java b/core/src/test/java/coffee/liz/ecs/common/components/physics/BoundingBoxTest.java new file mode 100644 index 0000000..2372456 --- /dev/null +++ b/core/src/test/java/coffee/liz/ecs/common/components/physics/BoundingBoxTest.java @@ -0,0 +1,45 @@ +package coffee.liz.ecs.common.components.physics; + +import coffee.liz.ecs.math.Vec2f; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class BoundingBoxTest { + @Test + public void isCollidingWith_overlapping() { + final BoundingBox a = new BoundingBox(new Vec2f(0f, 0f), new Vec2f(2f, 2f)); + final BoundingBox b = new BoundingBox(new Vec2f(1f, 1f), new Vec2f(2f, 2f)); + assertTrue(a.isCollidingWith(b)); + assertTrue(b.isCollidingWith(a)); + } + + @Test + public void isCollidingWith_notOverlapping() { + final BoundingBox a = new BoundingBox(new Vec2f(0f, 0f), new Vec2f(1f, 1f)); + final BoundingBox b = new BoundingBox(new Vec2f(2f, 2f), new Vec2f(1f, 1f)); + assertFalse(a.isCollidingWith(b)); + } + + @Test + public void isCollidingWith_touching_isNotColliding() { + final BoundingBox a = new BoundingBox(new Vec2f(0f, 0f), new Vec2f(1f, 1f)); + final BoundingBox b = new BoundingBox(new Vec2f(1f, 0f), new Vec2f(1f, 1f)); + assertFalse(a.isCollidingWith(b)); + } + + @Test + public void isAbove_returnsTrue_whenBottomAtOrAboveOtherTop() { + final BoundingBox a = new BoundingBox(new Vec2f(0f, 2f), new Vec2f(1f, 1f)); + final BoundingBox b = new BoundingBox(new Vec2f(0f, 0f), new Vec2f(1f, 1f)); + assertTrue(a.isAbove(b)); + } + + @Test + public void isAbove_returnsFalse_whenOverlapping() { + final BoundingBox a = new BoundingBox(new Vec2f(0f, 0.5f), new Vec2f(1f, 1f)); + final BoundingBox b = new BoundingBox(new Vec2f(0f, 0f), new Vec2f(1f, 1f)); + assertFalse(a.isAbove(b)); + } +} diff --git a/core/src/test/java/coffee/liz/ecs/common/systems/physics/CollisionGridTest.java b/core/src/test/java/coffee/liz/ecs/common/systems/physics/CollisionGridTest.java new file mode 100644 index 0000000..d5670aa --- /dev/null +++ b/core/src/test/java/coffee/liz/ecs/common/systems/physics/CollisionGridTest.java @@ -0,0 +1,50 @@ +package coffee.liz.ecs.common.systems.physics; + +import coffee.liz.ecs.common.components.physics.BoundingBox; +import coffee.liz.ecs.math.Vec2f; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CollisionGridTest { + private final CollisionGrid grid = new CollisionGrid(); + + @BeforeEach + public void setup() { + grid.setOrigin(new Vec2f(0f, 0f)); + grid.setCellSize(new Vec2f(32f, 32f)); + } + + @Test + public void insertedEntityIsFoundAsNeighbor() { + final BoundingBox bb = new BoundingBox(new Vec2f(0f, 0f), new Vec2f(1f, 1f)); + grid.insert(1, bb); + assertTrue(grid.getNeighborIds(bb).contains(1)); + } + + @Test + public void entityInDifferentCellIsNotNeighbor() { + final BoundingBox a = new BoundingBox(new Vec2f(0f, 0f), new Vec2f(1f, 1f)); + final BoundingBox b = new BoundingBox(new Vec2f(100f, 100f), new Vec2f(1f, 1f)); + grid.insert(1, a); + assertFalse(grid.getNeighborIds(b).contains(1)); + } + + @Test + public void clearRemovesAllEntities() { + final BoundingBox bb = new BoundingBox(new Vec2f(0f, 0f), new Vec2f(1f, 1f)); + grid.insert(1, bb); + grid.clear(); + assertTrue(grid.getNeighborIds(bb).isEmpty()); + } + + @Test + public void largeEntitySpanningMultipleCellsFoundByNeighborInAnyCell() { + final BoundingBox large = new BoundingBox(new Vec2f(0f, 0f), new Vec2f(64f, 64f)); + final BoundingBox corner = new BoundingBox(new Vec2f(50f, 50f), new Vec2f(1f, 1f)); + grid.insert(1, large); + assertTrue(grid.getNeighborIds(corner).contains(1)); + } +} diff --git a/core/src/test/java/coffee/liz/ecs/common/systems/physics/PhysicsSystemsTest.java b/core/src/test/java/coffee/liz/ecs/common/systems/physics/PhysicsSystemsTest.java new file mode 100644 index 0000000..13a4a1c --- /dev/null +++ b/core/src/test/java/coffee/liz/ecs/common/systems/physics/PhysicsSystemsTest.java @@ -0,0 +1,78 @@ +package coffee.liz.ecs.common.systems.physics; + +import coffee.liz.ecs.DAGWorld; +import coffee.liz.ecs.common.components.physics.BoundingBox; +import coffee.liz.ecs.common.components.physics.Collidable; +import coffee.liz.ecs.common.components.physics.Force; +import coffee.liz.ecs.common.components.physics.Forces; +import coffee.liz.ecs.common.components.physics.Gravity; +import coffee.liz.ecs.common.components.physics.Jump; +import coffee.liz.ecs.common.components.physics.Mass; +import coffee.liz.ecs.common.components.physics.TopCollidable; +import coffee.liz.ecs.common.components.physics.Velocity; +import coffee.liz.ecs.math.Vec2f; +import coffee.liz.ecs.model.Entity; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PhysicsSystemsTest { + private static final float GRAVITY = 9.8f; + + @Test + public void gravityAcceleratesEntityDownwardOverTime() { + final DAGWorld world = new DAGWorld(new ForceReductionSystem(GRAVITY), new IntegrationSystem()); + final Entity entity = world.createEntity(); + entity.add(new Mass(1f)); + entity.add(new Velocity(Vec2f.ZERO)); + entity.add(new Forces()); + entity.add(new Gravity(100f)); + entity.add(new BoundingBox(new Vec2f(0f, 10f), new Vec2f(1f, 1f))); + + world.update(1f); + + assertTrue(entity.get(Velocity.class).getVelocity().getY() < 0f); + assertTrue(entity.get(BoundingBox.class).getPosition().getY() < 10f); + } + + @Test + public void forcesAreClearedEachFrame() { + final DAGWorld world = new DAGWorld(new ForceReductionSystem(GRAVITY), new IntegrationSystem()); + final Entity entity = world.createEntity(); + entity.add(new Mass(1f)); + entity.add(new Velocity(Vec2f.ZERO)); + final Forces forces = new Forces(); + forces.add(new Force(new Vec2f(0f, 100f))); + entity.add(forces); + + world.update(1f); + + assertTrue(entity.get(Forces.class).getForces().isEmpty()); + } + + @Test + public void entityLandsOnPlatformAndStops() { + final DAGWorld world = new DAGWorld( + new ForceReductionSystem(GRAVITY), new IntegrationSystem(), new CollisionSystem(GRAVITY, 32f)); + + final Entity player = world.createEntity(); + player.add(new Mass(1f)); + player.add(new Velocity(new Vec2f(0f, -5f))); + player.add(new Forces()); + player.add(new Gravity(100f)); + player.add(new Jump(false, 0L)); + player.add(new BoundingBox(new Vec2f(0f, 1.1f), new Vec2f(1f, 1f))); + player.add(new Collidable()); + + final Entity floor = world.createEntity(); + floor.add(new BoundingBox(new Vec2f(-5f, 0f), new Vec2f(10f, 1f))); + floor.add(new TopCollidable()); + + world.update(0.1f); + + assertEquals(1f, player.get(BoundingBox.class).getPosition().getY(), 0.001f); + assertEquals(0f, player.get(Velocity.class).getVelocity().getY(), 0.001f); + assertTrue(player.get(Jump.class).isCanJump()); + } +} |
