summaryrefslogtreecommitdiff
path: root/core/src/main/java/coffee/liz/ecs
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/coffee/liz/ecs')
-rw-r--r--core/src/main/java/coffee/liz/ecs/DAGWorld.java74
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/components/physics/BoundingBox.java34
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/components/physics/Collidable.java6
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/components/physics/Force.java12
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/components/physics/Forces.java20
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/components/physics/Gravity.java11
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/components/physics/Jump.java14
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/components/physics/Mass.java11
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/components/physics/TopCollidable.java6
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/components/physics/Velocity.java14
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/systems/physics/CollisionGrid.java65
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/systems/physics/CollisionSystem.java107
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/systems/physics/ForceReductionSystem.java55
-rw-r--r--core/src/main/java/coffee/liz/ecs/common/systems/physics/IntegrationSystem.java25
-rw-r--r--core/src/main/java/coffee/liz/ecs/model/QueryBuilder.java4
-rw-r--r--core/src/main/java/coffee/liz/ecs/model/System.java25
-rw-r--r--core/src/main/java/coffee/liz/ecs/model/World.java56
17 files changed, 423 insertions, 116 deletions
diff --git a/core/src/main/java/coffee/liz/ecs/DAGWorld.java b/core/src/main/java/coffee/liz/ecs/DAGWorld.java
index 0cc7d5d..f941dba 100644
--- a/core/src/main/java/coffee/liz/ecs/DAGWorld.java
+++ b/core/src/main/java/coffee/liz/ecs/DAGWorld.java
@@ -8,7 +8,6 @@ import coffee.liz.ecs.model.QueryBuilder;
import coffee.liz.ecs.model.System;
import coffee.liz.ecs.model.World;
-import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import java.util.ArrayList;
@@ -26,20 +25,17 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-/** World that updates in {@link System#getDependencies()} topological order. */
@Log4j2
-@RequiredArgsConstructor
-public class DAGWorld<T> implements World<T> {
+public class DAGWorld implements World {
private final Set<Entity> entities = Collections.synchronizedSet(new HashSet<>());
private final ComponentCache componentCache = new ComponentCache();
private final Consumer<EntityEvent> entityEventConsumer = componentCache::onEntityEvent;
private final AtomicInteger nextEntityId = new AtomicInteger(0);
- protected final Map<Class<? extends System<T>>, System<T>> systems;
- private final List<System<T>> systemExecutionOrder;
- private final QueryBuilder<T> queryBuilder = new QueryBuilder<>(this);
+ protected final Map<Class<? extends System>, System> systems;
+ private final List<System> systemExecutionOrder;
+ private final QueryBuilder queryBuilder = new QueryBuilder(this);
- @SafeVarargs
- public DAGWorld(final System<T>... systems) {
+ public DAGWorld(final System... systems) {
this.systems = singletonClazzMap(systems);
this.systemExecutionOrder = buildExecutionOrder(Arrays.asList(systems));
log.info("Executing in order: {}", systemExecutionOrder);
@@ -72,18 +68,18 @@ public class DAGWorld<T> implements World<T> {
}
@Override
- public QueryBuilder<T> queryable() {
+ public QueryBuilder queryable() {
return queryBuilder;
}
@Override
- public void update(final T state, final float deltaSeconds) {
- systemExecutionOrder.forEach(system -> system.update(this, state, deltaSeconds));
+ public void update(final float deltaSeconds) {
+ systemExecutionOrder.forEach(system -> system.update(this, deltaSeconds));
}
@SuppressWarnings("unchecked")
@Override
- public <S extends System<T>> S getSystem(final Class<S> system) {
+ public <S extends System> S getSystem(final Class<S> system) {
return (S) systems.get(system);
}
@@ -98,7 +94,6 @@ public class DAGWorld<T> implements World<T> {
if (components.isEmpty()) {
return Collections.emptySet();
}
-
final Set<Entity> matches = new HashSet<>();
components.forEach(componentType -> matches.addAll(componentCache.entitiesWith(componentType)));
return matches;
@@ -108,10 +103,8 @@ public class DAGWorld<T> implements World<T> {
if (components.isEmpty()) {
return entities;
}
-
final Set<Entity> excluded = new HashSet<>();
components.forEach(componentType -> excluded.addAll(componentCache.entitiesWith(componentType)));
-
final Set<Entity> result = new HashSet<>();
entities.forEach(entity -> {
if (!excluded.contains(entity)) {
@@ -121,41 +114,35 @@ public class DAGWorld<T> implements World<T> {
return result;
}
- private List<System<T>> buildExecutionOrder(final Collection<System<T>> systems) {
+ private List<System> buildExecutionOrder(final Collection<System> systems) {
if (systems.isEmpty()) {
return Collections.emptyList();
}
- final Map<Class<?>, System<T>> systemMap = systems.stream()
+ final Map<Class<?>, System> systemMap = systems.stream()
.collect(Collectors.toMap(System::getClass, system -> system, (_sys, b) -> b, LinkedHashMap::new));
final Map<Class<?>, Integer> inDegree = new LinkedHashMap<>();
final Map<Class<?>, Set<Class<?>>> adjacencyList = new LinkedHashMap<>();
systems.forEach(system -> {
- final Class<?> systemClass = system.getClass();
- inDegree.put(systemClass, 0);
- adjacencyList.put(systemClass, new HashSet<>());
- });
-
- systems.forEach(system -> {
- system.getDependencies().forEach(dependency -> {
- if (systemMap.containsKey(dependency)) {
- adjacencyList.get(dependency).add(system.getClass());
- inDegree.merge(system.getClass(), 1, Integer::sum);
- }
- });
+ inDegree.put(system.getClass(), 0);
+ adjacencyList.put(system.getClass(), new HashSet<>());
});
- // Kahn's algorithm
- final List<System<T>> result = new ArrayList<>();
+ systems.forEach(system -> system.getDependencies().forEach(dependency -> {
+ if (systemMap.containsKey(dependency)) {
+ adjacencyList.get(dependency).add(system.getClass());
+ inDegree.merge(system.getClass(), 1, Integer::sum);
+ }
+ }));
+ final List<System> result = new ArrayList<>();
final Queue<Class<?>> queue = new LinkedList<>(
- inDegree.entrySet().stream().filter(entry -> entry.getValue() == 0).map(Map.Entry::getKey).toList());
+ inDegree.entrySet().stream().filter(e -> e.getValue() == 0).map(Map.Entry::getKey).toList());
while (!queue.isEmpty()) {
final Class<?> currentClass = queue.poll();
result.add(systemMap.get(currentClass));
-
adjacencyList.getOrDefault(currentClass, Collections.emptySet()).forEach(dependent -> {
final int newInDegree = inDegree.get(dependent) - 1;
inDegree.put(dependent, newInDegree);
@@ -174,23 +161,24 @@ public class DAGWorld<T> implements World<T> {
@Override
public void dispose() {
- for (final System<T> system : systemExecutionOrder) {
- system.dispose();
- }
+ systemExecutionOrder.forEach(System::dispose);
entities.forEach(entity -> entity.unsubscribe(entityEventConsumer));
componentCache.clear();
entities.clear();
}
@SuppressWarnings("unchecked")
- private static <T> Map<Class<? extends T>, T> singletonClazzMap(final T... singletons) {
- final boolean areSingletons = Arrays.stream(singletons).map(t -> (Class<? extends System<T>>) t.getClass())
- .distinct().count() == singletons.length;
+ private static Map<Class<? extends System>, System> singletonClazzMap(final System... singletons) {
+ final boolean areSingletons = Arrays.stream(singletons)
+ .map(Object::getClass)
+ .distinct()
+ .count() == singletons.length;
if (!areSingletons) {
throw new IllegalArgumentException("Only one instance may be used per clazz");
}
-
- return Arrays.stream(singletons).map(t -> Map.entry((Class<? extends T>) t.getClass(), t))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ return Arrays.stream(singletons)
+ .collect(Collectors.toMap(
+ s -> (Class<? extends System>) s.getClass(),
+ s -> s));
}
}
diff --git a/core/src/main/java/coffee/liz/ecs/common/components/physics/BoundingBox.java b/core/src/main/java/coffee/liz/ecs/common/components/physics/BoundingBox.java
new file mode 100644
index 0000000..7a31cae
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/components/physics/BoundingBox.java
@@ -0,0 +1,34 @@
+package coffee.liz.ecs.common.components.physics;
+
+import coffee.liz.ecs.math.Vec2;
+import coffee.liz.ecs.model.Component;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class BoundingBox implements Component {
+ private Vec2<Float> position;
+ private Vec2<Float> size;
+
+ public float getTop() {
+ return position.getY() + size.getY();
+ }
+
+ public float getRight() {
+ return position.getX() + size.getX();
+ }
+
+ public boolean isCollidingWith(final BoundingBox other) {
+ return getRight() > other.getPosition().getX()
+ && position.getX() < other.getRight()
+ && getTop() > other.getPosition().getY()
+ && position.getY() < other.getTop();
+ }
+
+ public boolean isAbove(final BoundingBox other) {
+ return position.getY() >= other.getTop();
+ }
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/components/physics/Collidable.java b/core/src/main/java/coffee/liz/ecs/common/components/physics/Collidable.java
new file mode 100644
index 0000000..166428a
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/components/physics/Collidable.java
@@ -0,0 +1,6 @@
+package coffee.liz.ecs.common.components.physics;
+
+import coffee.liz.ecs.model.Component;
+
+public class Collidable implements Component {
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/components/physics/Force.java b/core/src/main/java/coffee/liz/ecs/common/components/physics/Force.java
new file mode 100644
index 0000000..7d5dab1
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/components/physics/Force.java
@@ -0,0 +1,12 @@
+package coffee.liz.ecs.common.components.physics;
+
+import coffee.liz.ecs.math.Vec2;
+import coffee.liz.ecs.model.Component;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Getter
+public class Force implements Component {
+ private final Vec2<Float> force;
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/components/physics/Forces.java b/core/src/main/java/coffee/liz/ecs/common/components/physics/Forces.java
new file mode 100644
index 0000000..f6c9ee8
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/components/physics/Forces.java
@@ -0,0 +1,20 @@
+package coffee.liz.ecs.common.components.physics;
+
+import coffee.liz.ecs.model.Component;
+import lombok.Getter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+public class Forces implements Component {
+ private final List<Force> forces = new ArrayList<>();
+
+ public void add(final Force force) {
+ forces.add(force);
+ }
+
+ public void clear() {
+ forces.clear();
+ }
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/components/physics/Gravity.java b/core/src/main/java/coffee/liz/ecs/common/components/physics/Gravity.java
new file mode 100644
index 0000000..44d5caf
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/components/physics/Gravity.java
@@ -0,0 +1,11 @@
+package coffee.liz.ecs.common.components.physics;
+
+import coffee.liz.ecs.model.Component;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Getter
+public class Gravity implements Component {
+ private final float terminalVelocity;
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/components/physics/Jump.java b/core/src/main/java/coffee/liz/ecs/common/components/physics/Jump.java
new file mode 100644
index 0000000..5224b32
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/components/physics/Jump.java
@@ -0,0 +1,14 @@
+package coffee.liz.ecs.common.components.physics;
+
+import coffee.liz.ecs.model.Component;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class Jump implements Component {
+ private boolean canJump;
+ private long jumpStartMs;
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/components/physics/Mass.java b/core/src/main/java/coffee/liz/ecs/common/components/physics/Mass.java
new file mode 100644
index 0000000..f38ccc0
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/components/physics/Mass.java
@@ -0,0 +1,11 @@
+package coffee.liz.ecs.common.components.physics;
+
+import coffee.liz.ecs.model.Component;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Getter
+public class Mass implements Component {
+ private final float mass;
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/components/physics/TopCollidable.java b/core/src/main/java/coffee/liz/ecs/common/components/physics/TopCollidable.java
new file mode 100644
index 0000000..76185bd
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/components/physics/TopCollidable.java
@@ -0,0 +1,6 @@
+package coffee.liz.ecs.common.components.physics;
+
+import coffee.liz.ecs.model.Component;
+
+public class TopCollidable implements Component {
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/components/physics/Velocity.java b/core/src/main/java/coffee/liz/ecs/common/components/physics/Velocity.java
new file mode 100644
index 0000000..9b6a36b
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/components/physics/Velocity.java
@@ -0,0 +1,14 @@
+package coffee.liz.ecs.common.components.physics;
+
+import coffee.liz.ecs.math.Vec2;
+import coffee.liz.ecs.model.Component;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class Velocity implements Component {
+ private Vec2<Float> velocity;
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/systems/physics/CollisionGrid.java b/core/src/main/java/coffee/liz/ecs/common/systems/physics/CollisionGrid.java
new file mode 100644
index 0000000..94202b4
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/systems/physics/CollisionGrid.java
@@ -0,0 +1,65 @@
+package coffee.liz.ecs.common.systems.physics;
+
+import coffee.liz.ecs.common.components.physics.BoundingBox;
+import coffee.liz.ecs.math.Vec2;
+import coffee.liz.ecs.math.Vec2i;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class CollisionGrid {
+ private final Map<Vec2i, Set<Integer>> cells = new HashMap<>();
+ private Vec2<Float> origin;
+ private Vec2<Float> cellSize;
+
+ public void setOrigin(final Vec2<Float> origin) {
+ this.origin = origin;
+ }
+
+ public void setCellSize(final Vec2<Float> cellSize) {
+ this.cellSize = cellSize;
+ }
+
+ public void clear() {
+ cells.clear();
+ }
+
+ public void insert(final int entityId, final BoundingBox bb) {
+ final int minCx = cellX(bb.getPosition().getX());
+ final int minCy = cellY(bb.getPosition().getY());
+ final int maxCx = cellX(bb.getRight());
+ final int maxCy = cellY(bb.getTop());
+ for (int cx = minCx; cx <= maxCx; cx++) {
+ for (int cy = minCy; cy <= maxCy; cy++) {
+ cells.computeIfAbsent(new Vec2i(cx, cy), _ -> new HashSet<>()).add(entityId);
+ }
+ }
+ }
+
+ public Set<Integer> getNeighborIds(final BoundingBox bb) {
+ final Set<Integer> neighbors = new HashSet<>();
+ final int minCx = cellX(bb.getPosition().getX());
+ final int minCy = cellY(bb.getPosition().getY());
+ final int maxCx = cellX(bb.getRight());
+ final int maxCy = cellY(bb.getTop());
+ for (int cx = minCx; cx <= maxCx; cx++) {
+ for (int cy = minCy; cy <= maxCy; cy++) {
+ final Set<Integer> cell = cells.get(new Vec2i(cx, cy));
+ if (cell != null) {
+ neighbors.addAll(cell);
+ }
+ }
+ }
+ return neighbors;
+ }
+
+ private int cellX(final float x) {
+ return (int) Math.floor((x - origin.getX()) / cellSize.getX());
+ }
+
+ private int cellY(final float y) {
+ return (int) Math.floor((y - origin.getY()) / cellSize.getY());
+ }
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/systems/physics/CollisionSystem.java b/core/src/main/java/coffee/liz/ecs/common/systems/physics/CollisionSystem.java
new file mode 100644
index 0000000..14f0a2b
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/systems/physics/CollisionSystem.java
@@ -0,0 +1,107 @@
+package coffee.liz.ecs.common.systems.physics;
+
+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.math.Vec2i;
+import coffee.liz.ecs.model.Entity;
+import coffee.liz.ecs.model.System;
+import coffee.liz.ecs.model.World;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@RequiredArgsConstructor
+@AllArgsConstructor
+public class CollisionSystem implements System {
+ private final float gravity;
+ private float cellSize = 64f;
+ private final CollisionGrid grid = new CollisionGrid();
+
+ @Override
+ public Collection<Class<? extends System>> getDependencies() {
+ return Set.of(IntegrationSystem.class);
+ }
+
+ @Override
+ public void update(final World world, final float deltaSeconds) {
+ final Set<Entity> collidableEntities = world.queryable().allOf(BoundingBox.class, Collidable.class);
+ final Set<Entity> surfaceEntities = world.queryable().allOf(BoundingBox.class, TopCollidable.class);
+
+ if (collidableEntities.isEmpty() || surfaceEntities.isEmpty()) {
+ return;
+ }
+
+ final Map<Integer, Entity> entityMap = new HashMap<>();
+ collidableEntities.forEach(e -> entityMap.put(e.getId(), e));
+ surfaceEntities.forEach(e -> entityMap.put(e.getId(), e));
+
+ float minX = Float.MAX_VALUE, minY = Float.MAX_VALUE;
+ for (final Entity e : entityMap.values()) {
+ final BoundingBox bb = e.get(BoundingBox.class);
+ minX = Math.min(minX, bb.getPosition().getX());
+ minY = Math.min(minY, bb.getPosition().getY());
+ }
+
+ grid.clear();
+ grid.setOrigin(new Vec2f(minX, minY));
+ grid.setCellSize(new Vec2f(cellSize, cellSize));
+ entityMap.forEach((id, e) -> grid.insert(id, e.get(BoundingBox.class)));
+
+ final Set<Vec2i> checkedPairs = new HashSet<>();
+
+ for (final Entity entityA : collidableEntities) {
+ if (!entityA.has(Velocity.class)) {
+ continue;
+ }
+ final BoundingBox bbA = entityA.get(BoundingBox.class);
+ final Velocity velocity = entityA.get(Velocity.class);
+
+ for (final int idB : grid.getNeighborIds(bbA)) {
+ final int idA = entityA.getId();
+ if (idB == idA) {
+ continue;
+ }
+ if (!checkedPairs.add(new Vec2i(Math.min(idA, idB), Math.max(idA, idB)))) {
+ continue;
+ }
+
+ final Entity entityB = entityMap.get(idB);
+ if (entityB == null || !entityB.has(TopCollidable.class)) {
+ continue;
+ }
+
+ final BoundingBox bbB = entityB.get(BoundingBox.class);
+ if (!bbA.isCollidingWith(bbB)) {
+ continue;
+ }
+ if (velocity.getVelocity().getY() > 0 || bbA.isAbove(bbB)) {
+ continue;
+ }
+
+ bbA.setPosition(new Vec2f(bbA.getPosition().getX(), bbB.getTop()));
+ velocity.setVelocity(new Vec2f(velocity.getVelocity().getX(), 0f));
+
+ if (entityA.has(Jump.class)) {
+ entityA.get(Jump.class).setCanJump(true);
+ }
+ if (entityA.has(Gravity.class) && entityA.has(Forces.class) && entityA.has(Mass.class)) {
+ entityA.get(Forces.class).add(new Force(new Vec2f(0f, entityA.get(Mass.class).getMass() * gravity)));
+ }
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/systems/physics/ForceReductionSystem.java b/core/src/main/java/coffee/liz/ecs/common/systems/physics/ForceReductionSystem.java
new file mode 100644
index 0000000..4a51ba8
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/systems/physics/ForceReductionSystem.java
@@ -0,0 +1,55 @@
+package coffee.liz.ecs.common.systems.physics;
+
+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.Velocity;
+import coffee.liz.ecs.math.Vec2;
+import coffee.liz.ecs.math.Vec2f;
+import coffee.liz.ecs.model.System;
+import coffee.liz.ecs.model.World;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Collection;
+import java.util.Set;
+
+@RequiredArgsConstructor
+public class ForceReductionSystem implements System {
+ private final float gravity;
+
+ @Override
+ public Collection<Class<? extends System>> getDependencies() {
+ return Set.of();
+ }
+
+ @Override
+ public void update(final World world, final float deltaSeconds) {
+ world.queryable().allOf(Forces.class, Mass.class, Velocity.class).forEach(entity -> {
+ final Forces forces = entity.get(Forces.class);
+ final Mass mass = entity.get(Mass.class);
+ final Velocity velocity = entity.get(Velocity.class);
+
+ if (entity.has(Gravity.class)) {
+ final Gravity gravityComponent = entity.get(Gravity.class);
+ if (velocity.getVelocity().getY() > -gravityComponent.getTerminalVelocity()) {
+ forces.add(new Force(new Vec2f(0f, -mass.getMass() * gravity)));
+ }
+ }
+
+ Vec2<Float> netForce = Vec2f.ZERO;
+ for (final Force f : forces.getForces()) {
+ netForce = netForce.plus(f.getForce());
+ }
+ forces.clear();
+
+ final Vec2<Float> acceleration = netForce.scale(1f / mass.getMass(), 1f / mass.getMass());
+ velocity.setVelocity(velocity.getVelocity().plus(acceleration.scale(deltaSeconds, deltaSeconds)));
+
+ if (entity.has(Jump.class) && acceleration.getY() < 0) {
+ entity.get(Jump.class).setCanJump(false);
+ }
+ });
+ }
+}
diff --git a/core/src/main/java/coffee/liz/ecs/common/systems/physics/IntegrationSystem.java b/core/src/main/java/coffee/liz/ecs/common/systems/physics/IntegrationSystem.java
new file mode 100644
index 0000000..ba38b70
--- /dev/null
+++ b/core/src/main/java/coffee/liz/ecs/common/systems/physics/IntegrationSystem.java
@@ -0,0 +1,25 @@
+package coffee.liz.ecs.common.systems.physics;
+
+import coffee.liz.ecs.common.components.physics.BoundingBox;
+import coffee.liz.ecs.common.components.physics.Velocity;
+import coffee.liz.ecs.model.System;
+import coffee.liz.ecs.model.World;
+
+import java.util.Collection;
+import java.util.Set;
+
+public class IntegrationSystem implements System {
+ @Override
+ public Collection<Class<? extends System>> getDependencies() {
+ return Set.of(ForceReductionSystem.class);
+ }
+
+ @Override
+ public void update(final World world, final float deltaSeconds) {
+ world.queryable().allOf(Velocity.class, BoundingBox.class).forEach(entity -> {
+ final BoundingBox bb = entity.get(BoundingBox.class);
+ final Velocity velocity = entity.get(Velocity.class);
+ bb.setPosition(bb.getPosition().plus(velocity.getVelocity().scale(deltaSeconds, deltaSeconds)));
+ });
+ }
+}
diff --git a/core/src/main/java/coffee/liz/ecs/model/QueryBuilder.java b/core/src/main/java/coffee/liz/ecs/model/QueryBuilder.java
index eba5021..bc7c4f7 100644
--- a/core/src/main/java/coffee/liz/ecs/model/QueryBuilder.java
+++ b/core/src/main/java/coffee/liz/ecs/model/QueryBuilder.java
@@ -5,8 +5,8 @@ import lombok.RequiredArgsConstructor;
import java.util.Set;
@RequiredArgsConstructor
-public class QueryBuilder<T> {
- private final World<T> world;
+public class QueryBuilder {
+ private final World world;
@SafeVarargs
public final Set<Entity> allOf(final Class<? extends Component>... components) {
diff --git a/core/src/main/java/coffee/liz/ecs/model/System.java b/core/src/main/java/coffee/liz/ecs/model/System.java
index f16cdba..e9e0729 100644
--- a/core/src/main/java/coffee/liz/ecs/model/System.java
+++ b/core/src/main/java/coffee/liz/ecs/model/System.java
@@ -2,29 +2,10 @@ package coffee.liz.ecs.model;
import java.util.Collection;
-/**
- * Updates the {@link World} state.
- *
- * @param <T>
- * is the state of the stuff outside the {@link World}.
- */
-public interface System<T> {
- /**
- * {@link System} clazzes that must run before this system.
- *
- * @return {@link Collection} of dependencies.
- */
- Collection<Class<? extends System<T>>> getDependencies();
+public interface System {
+ Collection<Class<? extends System>> getDependencies();
- /**
- * @param world
- * Is the {@link World}.
- * @param state
- * Is the {@link T} state outside the {@param world}.
- * @param deltaSeconds
- * Is the timestamp.
- */
- void update(final World<T> world, final T state, final float deltaSeconds);
+ void update(World world, float deltaSeconds);
default void dispose() {
}
diff --git a/core/src/main/java/coffee/liz/ecs/model/World.java b/core/src/main/java/coffee/liz/ecs/model/World.java
index 82e01a7..63335c8 100644
--- a/core/src/main/java/coffee/liz/ecs/model/World.java
+++ b/core/src/main/java/coffee/liz/ecs/model/World.java
@@ -1,63 +1,21 @@
package coffee.liz.ecs.model;
-import java.time.Duration;
import java.util.Set;
-/**
- * The game world.
- *
- * @param <T>
- * is the state of the stuff outside the world.
- */
-public interface World<T> {
- /**
- * Create unique {@link Entity} in the {@link World}.
- *
- * @return created {@link Entity}.
- */
+public interface World {
Entity createEntity();
- /**
- * Remove an entity from the {@link World}.
- *
- * @param entity
- * to remove.
- */
- void removeEntity(final Entity entity);
+ void removeEntity(Entity entity);
- /**
- * Get entities with {@link Component}s.
- *
- * @param query
- * to query.
- * @return All entities satisfying {@param query}.
- */
- Set<Entity> resolve(final Query query);
+ Set<Entity> resolve(Query query);
- default QueryBuilder<T> queryable() {
- return new QueryBuilder<>(this);
+ default QueryBuilder queryable() {
+ return new QueryBuilder(this);
}
- /**
- * Integrate the {@link World}.
- *
- * @param state
- * Is the state outside the world.
- * @param deltaSeconds
- * Is the time step.
- */
- void update(T state, float deltaSeconds);
+ void update(float deltaSeconds);
- /**
- * Get world {@link System}.
- *
- * @param system
- * is the Clazz.
- * @param <S>
- * is the {@link System} type.
- * @return {@link System} instance of {@param system}.
- */
- <S extends System<T>> S getSystem(final Class<S> system);
+ <S extends System> S getSystem(Class<S> system);
default void dispose() {
}