diff options
| author | Elizabeth Hunt <me@liz.coffee> | 2026-01-31 20:21:47 -0800 |
|---|---|---|
| committer | Elizabeth Hunt <me@liz.coffee> | 2026-01-31 20:30:01 -0800 |
| commit | 40a7ea05a9d6041842a081bb5a98290a31ea798e (patch) | |
| tree | 94918f72763459d225dd71118ca777e6b51e31b1 | |
| parent | ddfa2b7d9ee1547090e568e843e327cacd27fc22 (diff) | |
| download | the-abstraction-engine-v2-40a7ea05a9d6041842a081bb5a98290a31ea798e.tar.gz the-abstraction-engine-v2-40a7ea05a9d6041842a081bb5a98290a31ea798e.zip | |
12 files changed, 447 insertions, 108 deletions
diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/GridBoardActor.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/GridBoardActor.java new file mode 100644 index 0000000..3c7a82a --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/GridBoardActor.java @@ -0,0 +1,68 @@ +package coffee.liz.abstractionengine.app.actor; + +import coffee.liz.abstractionengine.app.Theme; +import coffee.liz.abstractionengine.entity.EntityType; +import coffee.liz.ecs.math.Mat2; +import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.math.Vec2f; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; +import com.badlogic.gdx.scenes.scene2d.Actor; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@RequiredArgsConstructor +public class GridBoardActor extends Actor { + private final ShapeRenderer shapeRenderer; + private final Vec2<Integer> gridSize; + + @Setter + private Vec2<Float> cellSize; + + @Setter + private List<RenderableEntity> entities = List.of(); + + @Override + public void draw(final Batch batch, final float parentAlpha) { + batch.end(); + + shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); + shapeRenderer.setTransformMatrix(batch.getTransformMatrix()); + shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); + + if (cellSize != null) { + final float dotRadius = Math.min(cellSize.getX(), cellSize.getY()) * 0.07f; + shapeRenderer.setColor(Theme.BG_PATTERN); + Mat2.init(gridSize, pos -> { + final Vec2<Float> center = pos.floatValue().plus(Vec2f.builder().x(0.5f).y(0.5f).build()) + .scale(cellSize); + shapeRenderer.circle(center.getX(), center.getY(), dotRadius); + return center; + }); + } + + for (final RenderableEntity entity : entities) { + shapeRenderer.setColor(getEntityColor(entity.entityType())); + shapeRenderer.rect(entity.position().getX(), entity.position().getY(), entity.size().getX(), + entity.size().getY()); + } + + shapeRenderer.end(); + batch.begin(); + } + + private static Color getEntityColor(final EntityType type) { + return switch (type) { + case WALL -> Theme.MUTED; + case PLAYER -> Theme.PRIMARY; + case ABSTRACTION -> Theme.SECONDARY; + default -> Color.GREEN; + }; + } + + public record RenderableEntity(EntityType entityType, Vec2<Float> position, Vec2<Float> size) { + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java b/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java index 3d8be7a..01d84a3 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java @@ -9,7 +9,7 @@ import lombok.Getter; @Getter public class Settings { @Builder.Default - private boolean skipIntro = true; + private boolean skipIntro = false; @Builder.Default private boolean playMusic = true; diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java index f9402d1..3ddef91 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java @@ -1,150 +1,64 @@ package coffee.liz.abstractionengine.app.screen; -import coffee.liz.abstractionengine.AbstractionEngineGridWorld; import coffee.liz.abstractionengine.app.AbstractionEngineGame; -import coffee.liz.abstractionengine.app.Theme; -import coffee.liz.abstractionengine.entity.EntityFactory; -import coffee.liz.abstractionengine.entity.EntityType; -import coffee.liz.abstractionengine.grid.component.GridInputState; -import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.abstractionengine.app.screen.game.FrameState; +import coffee.liz.abstractionengine.app.screen.game.GameScreenWorld; +import coffee.liz.abstractionengine.app.screen.game.system.GameWorldManagementSystem; +import coffee.liz.abstractionengine.app.utils.FunctionUtils; import coffee.liz.ecs.math.Vec2; -import coffee.liz.ecs.math.Vec2f; import coffee.liz.ecs.math.Vec2i; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Screen; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.glutils.ShapeRenderer; -import com.badlogic.gdx.utils.ScreenUtils; +import com.badlogic.gdx.utils.viewport.FitViewport; import lombok.RequiredArgsConstructor; import java.time.Duration; -import java.util.Set; @RequiredArgsConstructor public class GameScreen implements Screen { private final AbstractionEngineGame game; - private static final Vec2<Integer> GAME_GRID_SIZE = Vec2i.builder().x(30).y(30).build(); - private final static Duration GRID_INTERPOLATION_TIME = Duration.ofMillis(60); + private static final Vec2<Integer> GAME_GRID_SIZE = Vec2i.builder().x(20).y(20).build(); - private final AbstractionEngineGridWorld gameWorld = new AbstractionEngineGridWorld(GAME_GRID_SIZE); - private Duration sinceUpdate = Duration.ZERO; + private GameScreenWorld outerWorld; + private FitViewport viewport; - /** - * Screen lifecycle hook. - */ @Override public void show() { - EntityFactory.populateRect(GAME_GRID_SIZE, gameWorld, EntityFactory.Wall::addToWorld); - EntityFactory.Player.addToWorld(gameWorld, Vec2i.builder().y(20).x(10).build()); - EntityFactory.Abstraction.addToWorld(gameWorld, Vec2i.builder().y(20).x(12).build()); - EntityFactory.Abstraction.addToWorld(gameWorld, Vec2i.builder().y(20).x(13).build()); + viewport = new FitViewport(AbstractionEngineGame.WORLD_SIZE.getX(), AbstractionEngineGame.WORLD_SIZE.getY()); + outerWorld = new GameScreenWorld(GAME_GRID_SIZE, viewport); + outerWorld.getSystem(GameWorldManagementSystem.class).populateLevel(); + viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); + render(0); } - /** - * Screen render hook. - * - * @param delta - * time since last frame - */ @Override public void render(final float delta) { - game.viewport.apply(); - sinceUpdate = sinceUpdate.plus(Duration.ofMillis((int) (delta * 1000))); - if (GRID_INTERPOLATION_TIME.compareTo(sinceUpdate) > 0) { - return; - } else { - sinceUpdate = Duration.ZERO; - } - - ScreenUtils.clear(Theme.BG); - - final GridInputState state = GridInputState.builder().movement(getMovementVector()).build(); - gameWorld.update(state); - - final Vec2<Float> cellSize = computeCellSize(); - game.shapeRenderer.setProjectionMatrix(game.batch.getProjectionMatrix()); - game.shapeRenderer.setTransformMatrix(game.batch.getTransformMatrix()); - game.shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); - gameWorld.query(Set.of(GridPosition.class, EntityType.class)).forEach(entity -> { - final EntityType type = entity.get(EntityType.class); - final GridPosition position = entity.get(GridPosition.class); - - final Vec2<Float> drawPos = position.getPosition().floatValue().scale(cellSize); - game.shapeRenderer.setColor(getEntityColor(type)); - game.shapeRenderer.rect(drawPos.getX(), drawPos.getY(), cellSize.getX(), cellSize.getY()); - }); - game.shapeRenderer.end(); - } - - private Vec2<Integer> getMovementVector() { - return game.settings.getKeyBinds().filterActiveActions(Gdx.input::isKeyPressed).stream().reduce(Vec2i.ZERO, - (movement, action) -> { - final Vec2<Integer> velocity = switch (action) { - case MOVE_UP -> Vec2i.NORTH; - case MOVE_DOWN -> Vec2i.SOUTH; - case MOVE_LEFT -> Vec2i.WEST; - case MOVE_RIGHT -> Vec2i.EAST; - }; - return movement.plus(velocity); - }, Vec2::plus); - } - - private Color getEntityColor(final EntityType type) { - return switch (type) { - case WALL -> Theme.BORDER; - case PLAYER -> Theme.PRIMARY; - case ABSTRACTION -> Theme.SECONDARY; - default -> Color.GREEN; - }; + viewport.apply(); + final FrameState state = FrameState.builder().settings(game.settings).isKeyPressed(Gdx.input::isKeyPressed) + .build(); + outerWorld.update(state, Duration.ofMillis((long) (delta * 1000))); } - /** - * Screen resize hook. - * - * @param width - * new width in pixels - * @param height - * new height in pixels - */ @Override public void resize(final int width, final int height) { - game.viewport.update(width, height); + viewport.update(width, height, true); } - /** - * Screen lifecycle hook. - */ @Override public void pause() { - } - /** - * Screen lifecycle hook. - */ @Override public void resume() { - } - /** - * Screen lifecycle hook. - */ @Override public void hide() { - + dispose(); } - /** - * Screen cleanup hook. - */ @Override public void dispose() { - - } - - private Vec2<Float> computeCellSize() { - return Vec2f.builder().x(game.viewport.getWorldWidth() / GAME_GRID_SIZE.getX()) - .y(game.viewport.getWorldHeight() / GAME_GRID_SIZE.getY()).build(); + FunctionUtils.wrapCheckedException(outerWorld::close); } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/FrameState.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/FrameState.java new file mode 100644 index 0000000..3f31530 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/FrameState.java @@ -0,0 +1,16 @@ +package coffee.liz.abstractionengine.app.screen.game; + +import coffee.liz.abstractionengine.app.config.Settings; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.function.Predicate; + +@Builder +@Getter +@RequiredArgsConstructor +public final class FrameState { + private final Settings settings; + private final Predicate<Integer> isKeyPressed; +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java new file mode 100644 index 0000000..f6385f6 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java @@ -0,0 +1,19 @@ +package coffee.liz.abstractionengine.app.screen.game; + +import coffee.liz.abstractionengine.app.screen.game.system.GameWorldManagementSystem; +import coffee.liz.abstractionengine.app.screen.game.system.GridInterpolationSystem; +import coffee.liz.abstractionengine.app.screen.game.system.InputSystem; +import coffee.liz.abstractionengine.app.screen.game.system.RenderSystem; +import coffee.liz.ecs.DAGWorld; +import coffee.liz.ecs.math.Vec2; +import com.badlogic.gdx.utils.viewport.FitViewport; + +import java.util.Map; + +public class GameScreenWorld extends DAGWorld<FrameState> { + public GameScreenWorld(final Vec2<Integer> gridSize, final FitViewport viewport) { + super(Map.of(InputSystem.class, new InputSystem(), GameWorldManagementSystem.class, + new GameWorldManagementSystem(gridSize), GridInterpolationSystem.class, new GridInterpolationSystem(), + RenderSystem.class, new RenderSystem(viewport, gridSize))); + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/GridEntityRef.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/GridEntityRef.java new file mode 100644 index 0000000..788d530 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/GridEntityRef.java @@ -0,0 +1,11 @@ +package coffee.liz.abstractionengine.app.screen.game.component; + +import coffee.liz.ecs.model.Component; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public final class GridEntityRef implements Component { + private final int gridEntityId; +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/InterpolatedPosition.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/InterpolatedPosition.java new file mode 100644 index 0000000..6700575 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/component/InterpolatedPosition.java @@ -0,0 +1,18 @@ +package coffee.liz.abstractionengine.app.screen.game.component; + +import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.model.Component; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public final class InterpolatedPosition implements Component { + private final Vec2<Float> from; + private final Vec2<Float> to; + private final Vec2<Float> current; + + public static InterpolatedPosition atRest(final Vec2<Float> position) { + return new InterpolatedPosition(position, position, position); + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GameWorldManagementSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GameWorldManagementSystem.java new file mode 100644 index 0000000..74355d1 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GameWorldManagementSystem.java @@ -0,0 +1,100 @@ +package coffee.liz.abstractionengine.app.screen.game.system; + +import coffee.liz.abstractionengine.AbstractionEngineGridWorld; +import coffee.liz.abstractionengine.app.screen.game.FrameState; +import coffee.liz.abstractionengine.app.screen.game.component.GridEntityRef; +import coffee.liz.abstractionengine.app.screen.game.component.InterpolatedPosition; +import coffee.liz.abstractionengine.app.utils.FunctionUtils; +import coffee.liz.abstractionengine.entity.EntityFactory; +import coffee.liz.abstractionengine.entity.EntityType; +import coffee.liz.abstractionengine.grid.component.GridInputState; +import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.ecs.math.Vec2; +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.Getter; + +import java.time.Duration; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class GameWorldManagementSystem implements System<FrameState> { + private final AbstractionEngineGridWorld gridWorld; + private final Vec2<Integer> gridSize; + private final Map<Integer, Entity> gridToOuterMap = new HashMap<>(); + + @Getter + private boolean stepped = false; + + public GameWorldManagementSystem(final Vec2<Integer> gridSize) { + this.gridSize = gridSize; + this.gridWorld = new AbstractionEngineGridWorld(gridSize); + } + + public void populateLevel() { + EntityFactory.populateRect(gridSize, gridWorld, EntityFactory.Wall::addToWorld); + EntityFactory.Player.addToWorld(gridWorld, Vec2i.builder().y(10).x(10).build()); + EntityFactory.Abstraction.addToWorld(gridWorld, Vec2i.builder().y(10).x(12).build()); + EntityFactory.Abstraction.addToWorld(gridWorld, Vec2i.builder().y(10).x(13).build()); + gridWorld.update(GridInputState.builder().movement(Vec2i.ZERO).build()); + } + + @Override + public Collection<Class<? extends System<FrameState>>> getDependencies() { + return Set.of(InputSystem.class); + } + + @Override + public void update(final World<FrameState> world, final FrameState state, final Duration dt) { + final InputSystem inputSystem = world.getSystem(InputSystem.class); + final Vec2<Integer> movement = inputSystem.getStepMovement(); + + stepped = !movement.equals(Vec2i.ZERO); + if (stepped) { + final GridInputState gridInput = GridInputState.builder().movement(movement).build(); + gridWorld.update(gridInput); + } + + syncEntities(world); + } + + private void syncEntities(final World<FrameState> outerWorld) { + final Set<Entity> gridEntities = gridWorld.query(Set.of(GridPosition.class, EntityType.class)); + final Set<Integer> seenGridIds = new HashSet<>(); + + for (final Entity gridEntity : gridEntities) { + seenGridIds.add(gridEntity.getId()); + + if (!gridToOuterMap.containsKey(gridEntity.getId())) { + final Entity outer = outerWorld.createEntity(); + final Vec2<Float> pos = gridEntity.get(GridPosition.class).getPosition().floatValue(); + outer.add(new GridEntityRef(gridEntity.getId())); + outer.add(gridEntity.get(EntityType.class)); + outer.add(InterpolatedPosition.atRest(pos)); + gridToOuterMap.put(gridEntity.getId(), outer); + } else if (stepped) { + final Entity outer = gridToOuterMap.get(gridEntity.getId()); + final InterpolatedPosition current = outer.get(InterpolatedPosition.class); + final Vec2<Float> newTarget = gridEntity.get(GridPosition.class).getPosition().floatValue(); + outer.add(new InterpolatedPosition(current.getCurrent(), newTarget, current.getCurrent())); + } + } + + final Set<Integer> removedIds = new HashSet<>(gridToOuterMap.keySet()); + removedIds.removeAll(seenGridIds); + for (final int removedId : removedIds) { + final Entity outer = gridToOuterMap.remove(removedId); + outerWorld.removeEntity(outer); + } + } + + @Override + public void close() { + FunctionUtils.wrapCheckedException(gridWorld::close); + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java new file mode 100644 index 0000000..2e4826d --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java @@ -0,0 +1,46 @@ +package coffee.liz.abstractionengine.app.screen.game.system; + +import coffee.liz.abstractionengine.app.screen.game.FrameState; +import coffee.liz.abstractionengine.app.screen.game.component.InterpolatedPosition; +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 java.time.Duration; +import java.util.Collection; +import java.util.Set; + +public class GridInterpolationSystem implements System<FrameState> { + private static final Duration INTERPOLATION_TIME = Duration.ofMillis(50); + + private Duration elapsed = Duration.ZERO; + + @Override + public Collection<Class<? extends System<FrameState>>> getDependencies() { + return Set.of(GameWorldManagementSystem.class); + } + + @Override + public void update(final World<FrameState> world, final FrameState state, final Duration dt) { + final GameWorldManagementSystem gameWorldSystem = world.getSystem(GameWorldManagementSystem.class); + + if (gameWorldSystem.isStepped()) { + elapsed = Duration.ZERO; + } + + elapsed = elapsed.plus(dt); + final float t = Math.min(1.0f, (float) elapsed.toMillis() / INTERPOLATION_TIME.toMillis()); + + world.query(Set.of(InterpolatedPosition.class)).forEach(entity -> { + final InterpolatedPosition pos = entity.get(InterpolatedPosition.class); + final Vec2<Float> interpolated = lerp(pos.getFrom(), pos.getTo(), t); + entity.add(new InterpolatedPosition(pos.getFrom(), pos.getTo(), interpolated)); + }); + } + + private static Vec2<Float> lerp(final Vec2<Float> from, final Vec2<Float> to, final float t) { + return Vec2f.builder().x(from.getX() + (to.getX() - from.getX()) * t) + .y(from.getY() + (to.getY() - from.getY()) * t).build(); + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/InputSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/InputSystem.java new file mode 100644 index 0000000..b3af099 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/InputSystem.java @@ -0,0 +1,73 @@ +package coffee.liz.abstractionengine.app.screen.game.system; + +import coffee.liz.abstractionengine.app.config.KeyBinds; +import coffee.liz.abstractionengine.app.screen.game.FrameState; +import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.math.Vec2i; +import coffee.liz.ecs.model.World; +import lombok.Getter; + +import java.time.Duration; +import java.util.Collection; +import java.util.Set; + +public class InputSystem implements coffee.liz.ecs.model.System<FrameState> { + private static final Duration INITIAL_DELAY = Duration.ofMillis(300); + private static final Duration REPEAT_INTERVAL = Duration.ofMillis(100); + + private KeyBinds.Action heldAction = null; + private Duration heldTime = Duration.ZERO; + private boolean pastInitialDelay = false; + + @Getter + private Vec2<Integer> stepMovement = Vec2i.ZERO; + + @Override + public Collection<Class<? extends coffee.liz.ecs.model.System<FrameState>>> getDependencies() { + return Set.of(); + } + + @Override + public void update(final World<FrameState> world, final FrameState state, final Duration dt) { + final Set<KeyBinds.Action> currentlyActive = state.getSettings().getKeyBinds() + .filterActiveActions(state.getIsKeyPressed()); + + if (currentlyActive.isEmpty()) { + heldAction = null; + stepMovement = Vec2i.ZERO; + return; + } + + final KeyBinds.Action active = currentlyActive.contains(heldAction) + ? heldAction + : currentlyActive.iterator().next(); + + if (!active.equals(heldAction)) { + heldAction = active; + heldTime = Duration.ZERO; + pastInitialDelay = false; + stepMovement = actionToMovement(active); + } else { + heldTime = heldTime.plus(dt); + if (!pastInitialDelay && heldTime.compareTo(INITIAL_DELAY) >= 0) { + pastInitialDelay = true; + heldTime = heldTime.minus(INITIAL_DELAY); + stepMovement = actionToMovement(active); + } else if (pastInitialDelay && heldTime.compareTo(REPEAT_INTERVAL) >= 0) { + heldTime = heldTime.minus(REPEAT_INTERVAL); + stepMovement = actionToMovement(active); + } else { + stepMovement = Vec2i.ZERO; + } + } + } + + private static Vec2<Integer> actionToMovement(final KeyBinds.Action action) { + return switch (action) { + case MOVE_UP -> Vec2i.NORTH; + case MOVE_DOWN -> Vec2i.SOUTH; + case MOVE_LEFT -> Vec2i.WEST; + case MOVE_RIGHT -> Vec2i.EAST; + }; + } +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java new file mode 100644 index 0000000..344271f --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java @@ -0,0 +1,74 @@ +package coffee.liz.abstractionengine.app.screen.game.system; + +import coffee.liz.abstractionengine.app.Theme; +import coffee.liz.abstractionengine.app.actor.GridBoardActor; +import coffee.liz.abstractionengine.app.screen.game.FrameState; +import coffee.liz.abstractionengine.app.screen.game.component.InterpolatedPosition; +import coffee.liz.abstractionengine.entity.EntityType; +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 com.badlogic.gdx.graphics.glutils.ShapeRenderer; +import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.utils.ScreenUtils; +import com.badlogic.gdx.utils.viewport.FitViewport; + +import java.time.Duration; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public class RenderSystem implements System<FrameState> { + private final FitViewport viewport; + private final Vec2<Integer> gridSize; + private final ShapeRenderer shapeRenderer; + private final Stage stage; + private final GridBoardActor boardActor; + + public RenderSystem(final FitViewport viewport, final Vec2<Integer> gridSize) { + this.viewport = viewport; + this.gridSize = gridSize; + this.shapeRenderer = new ShapeRenderer(); + this.stage = new Stage(viewport); + this.boardActor = new GridBoardActor(shapeRenderer, gridSize); + stage.addActor(boardActor); + } + + @Override + public Collection<Class<? extends System<FrameState>>> getDependencies() { + return Set.of(GridInterpolationSystem.class); + } + + @Override + public void update(final World<FrameState> world, final FrameState state, final Duration dt) { + ScreenUtils.clear(Theme.BG); + + final Vec2<Float> cellSize = computeCellSize(); + final List<GridBoardActor.RenderableEntity> renderables = world + .query(Set.of(InterpolatedPosition.class, EntityType.class)).stream().map(entity -> { + final InterpolatedPosition pos = entity.get(InterpolatedPosition.class); + final EntityType type = entity.get(EntityType.class); + final Vec2<Float> drawPos = pos.getCurrent().scale(cellSize); + return new GridBoardActor.RenderableEntity(type, drawPos, cellSize); + }).toList(); + + boardActor.setCellSize(cellSize); + boardActor.setEntities(renderables); + + final float deltaSeconds = dt.toMillis() / 1000f; + stage.act(deltaSeconds); + stage.draw(); + } + + @Override + public void close() { + stage.dispose(); + shapeRenderer.dispose(); + } + + private Vec2<Float> computeCellSize() { + return Vec2f.builder().x(viewport.getWorldWidth() / gridSize.getX()) + .y(viewport.getWorldHeight() / gridSize.getY()).build(); + } +} diff --git a/lwjgl3/src/main/java/coffee/liz/abstractionengine/lwjgl3/Lwjgl3Launcher.java b/lwjgl3/src/main/java/coffee/liz/abstractionengine/lwjgl3/Lwjgl3Launcher.java index 1c67c62..21b4a23 100644 --- a/lwjgl3/src/main/java/coffee/liz/abstractionengine/lwjgl3/Lwjgl3Launcher.java +++ b/lwjgl3/src/main/java/coffee/liz/abstractionengine/lwjgl3/Lwjgl3Launcher.java @@ -41,7 +41,7 @@ public class Lwjgl3Launcher { configuration.setHdpiMode(HdpiMode.Logical); configuration.setWindowedMode(1024, 1024); - configuration.setDecorated(true); + configuration.setDecorated(false); //// You can change these files; they are in lwjgl3/src/main/resources/ . //// They can also be loaded from the root of assets/ . configuration.setWindowIcon("libgdx128.png", "libgdx64.png", "libgdx32.png", "libgdx16.png"); |
