From 1b87af14b21bc3b3de411ed003f9420c6fd3c296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Tue, 2 Feb 2021 11:05:55 +0100 Subject: [PATCH] Replace Scene with GameMap | improve GameMap to allow variable layers From now on, GameMap is the central container for objects and is acting as a scene was before. What's more, GameMap is not restricted to 4 layers anymore. User can combine TileLayers (which define the terrain) and ObjectLayers (which can contain movable objects) together and is no limited to have only one ObjectLayer. Thanks to that it would be easier to implement i.e. birds which actually are a movable objects and are flying above the map and the highest terrain (top TileLayers in fact). --- .../base/core/world/animation/Animator.java | 2 +- .../core/world/animation/DefaultAnimator.java | 8 +- .../base/core/world/map/GameMap.java | 139 ++++++++++-------- .../base/core/world/map/Layer.java | 8 + .../base/core/world/map/ObjectLayer.java | 73 +++++++++ .../base/core/world/map/TileLayer.java | 44 ++++++ .../core/world/object/RenderableObject.java | 10 +- .../base/core/world/scene/Scene.java | 90 ------------ .../entity/manager/DefaultEntityManager.java | 6 +- .../world/entity/manager/EntityManager.java | 5 +- .../base/game/world/entity/model/Entity.java | 25 +--- 11 files changed, 219 insertions(+), 191 deletions(-) create mode 100755 engine/src/main/java/com/bartlomiejpluta/base/core/world/map/Layer.java create mode 100755 engine/src/main/java/com/bartlomiejpluta/base/core/world/map/ObjectLayer.java create mode 100755 engine/src/main/java/com/bartlomiejpluta/base/core/world/map/TileLayer.java delete mode 100755 engine/src/main/java/com/bartlomiejpluta/base/core/world/scene/Scene.java diff --git a/engine/src/main/java/com/bartlomiejpluta/base/core/world/animation/Animator.java b/engine/src/main/java/com/bartlomiejpluta/base/core/world/animation/Animator.java index 4a273ce5..7b9dcc3d 100755 --- a/engine/src/main/java/com/bartlomiejpluta/base/core/world/animation/Animator.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/core/world/animation/Animator.java @@ -1,5 +1,5 @@ package com.bartlomiejpluta.base.core.world.animation; public interface Animator { - void animate(Iterable objects); + void animate(AnimationableObject objects); } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/core/world/animation/DefaultAnimator.java b/engine/src/main/java/com/bartlomiejpluta/base/core/world/animation/DefaultAnimator.java index b4e2f9eb..bde76fb9 100755 --- a/engine/src/main/java/com/bartlomiejpluta/base/core/world/animation/DefaultAnimator.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/core/world/animation/DefaultAnimator.java @@ -6,13 +6,7 @@ import org.springframework.stereotype.Component; public class DefaultAnimator implements Animator { @Override - public void animate(Iterable objects) { - for (var object : objects) { - animate(object); - } - } - - private void animate(AnimationableObject object) { + public void animate(AnimationableObject object) { if(object.shouldAnimate()) { var positions = object.getSpriteAnimationFramesPositions(); var delay = object.getAnimationSpeed(); diff --git a/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/GameMap.java b/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/GameMap.java index 470abc81..c15e7122 100755 --- a/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/GameMap.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/GameMap.java @@ -1,106 +1,119 @@ package com.bartlomiejpluta.base.core.world.map; -import com.bartlomiejpluta.base.core.world.movement.Direction; +import com.bartlomiejpluta.base.core.gl.render.Renderable; +import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName; +import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager; +import com.bartlomiejpluta.base.core.logic.Updatable; +import com.bartlomiejpluta.base.core.ui.Window; +import com.bartlomiejpluta.base.core.world.animation.Animator; +import com.bartlomiejpluta.base.core.world.camera.Camera; +import com.bartlomiejpluta.base.core.world.movement.MovableObject; import com.bartlomiejpluta.base.core.world.movement.Movement; import com.bartlomiejpluta.base.core.world.tileset.model.Tile; import com.bartlomiejpluta.base.core.world.tileset.model.TileSet; import lombok.Getter; import org.joml.Vector2f; -import org.joml.Vector2i; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; -public class GameMap { - private static final int LAYERS = 4; +public class GameMap implements Renderable, Updatable { + private final Animator animator; + + private final Camera camera; private final TileSet tileSet; - private final Tile[][] map; + private final List layers = new ArrayList<>(); + private final float scale; - private final PassageAbility[] passageMap; - - @Getter - private final Vector2f stepSize; - @Getter private final int rows; @Getter - private final int cols; + private final int columns; - private GameMap(TileSet tileSet, int rows, int cols, float scale) { + @Getter + private final Vector2f stepSize; + + public GameMap(Animator animator, Camera camera, TileSet tileSet, int rows, int columns, float scale) { + this.animator = animator; + this.camera = camera; this.tileSet = tileSet; - this.rows = rows; - this.cols = cols; this.scale = scale; + this.rows = rows; + this.columns = columns; this.stepSize = new Vector2f(this.scale * this.tileSet.getTileWidth(), this.scale * this.tileSet.getTileHeight()); + } - map = new Tile[LAYERS][rows * cols]; - passageMap = new PassageAbility[rows * cols]; - Arrays.fill(passageMap, 0, rows * cols, PassageAbility.ALLOW); + @Override + public void render(Window window, ShaderManager shaderManager) { + shaderManager.setUniform(UniformName.UNI_PROJECTION_MATRIX, camera.getProjectionMatrix(window)); + shaderManager.setUniform(UniformName.UNI_VIEW_MATRIX, camera.getViewMatrix()); - for(int i=0; i(), passageMap)); + + return this; } - public Tile[] getLayer(int layer) { - return map[layer]; + public GameMap createTileLayer() { + layers.add(new TileLayer(new Tile[rows][columns], stepSize, scale)); + + return this; } - public void setPassageAbility(int row, int col, PassageAbility passageAbility) { - passageMap[row * cols + col] = passageAbility; + public GameMap addObject(int layerIndex, MovableObject object) { + ((ObjectLayer) layers.get(layerIndex)).addObject(object); + + return this; } - private PassageAbility getPassageAbility(Vector2i coordinates) { - return passageMap[coordinates.y * cols + coordinates.x]; + public GameMap removeObject(int layerIndex, MovableObject object) { + ((ObjectLayer) layers.get(layerIndex)).removeObject(object); + + return this; } - private PassageAbility getPassageAbility(int row, int col) { - return passageMap[row * cols + col]; + public GameMap setPassageAbility(int layerIndex, int row, int column, PassageAbility passageAbility) { + ((ObjectLayer) layers.get(layerIndex)).setPassageAbility(row, column, passageAbility); + return this; } - public boolean isMovementPossible(Movement movement) { - var source = movement.getSourceCoordinate(); + public GameMap setTile(int layerIndex, int row, int column, Tile tile) { + ((TileLayer) layers.get(layerIndex)).setTile(row, column, tile); + + return this; + } + + public boolean isMovementPossible(int layerIndex, Movement movement) { var target = movement.getTargetCoordinate(); + + // Is trying to go beyond the map + if(target.x < 0 || target.y < 0 || target.x >= columns || target.y >= rows) { + return false; + } + + var source = movement.getSourceCoordinate(); var direction = movement.getDirection(); - var isTargetReachable = switch(getPassageAbility(target)) { - case UP_ONLY -> direction != Direction.DOWN; - case DOWN_ONLY -> direction != Direction.UP; - case LEFT_ONLY -> direction != Direction.RIGHT; - case RIGHT_ONLY -> direction != Direction.LEFT; - case BLOCK -> false; - case ALLOW -> true; - }; - - var canMoveFromCurrentTile = switch(getPassageAbility(source)) { - case UP_ONLY -> direction == Direction.UP; - case DOWN_ONLY -> direction == Direction.DOWN; - case LEFT_ONLY -> direction == Direction.LEFT; - case RIGHT_ONLY -> direction == Direction.RIGHT; - default -> true; - }; - - return isTargetReachable && canMoveFromCurrentTile; - } - - public static GameMap empty(TileSet tileSet, int rows, int cols, float scale) { - return new GameMap(tileSet, rows, cols, scale); + return ((ObjectLayer) layers.get(layerIndex)).isMovementPossible(source, target, direction); } } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/Layer.java b/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/Layer.java new file mode 100755 index 00000000..cf011c5f --- /dev/null +++ b/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/Layer.java @@ -0,0 +1,8 @@ +package com.bartlomiejpluta.base.core.world.map; + +import com.bartlomiejpluta.base.core.gl.render.Renderable; +import com.bartlomiejpluta.base.core.logic.Updatable; + +public interface Layer extends Renderable, Updatable { + +} diff --git a/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/ObjectLayer.java b/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/ObjectLayer.java new file mode 100755 index 00000000..c84770a4 --- /dev/null +++ b/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/ObjectLayer.java @@ -0,0 +1,73 @@ +package com.bartlomiejpluta.base.core.world.map; + +import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName; +import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager; +import com.bartlomiejpluta.base.core.ui.Window; +import com.bartlomiejpluta.base.core.world.animation.Animator; +import com.bartlomiejpluta.base.core.world.movement.Direction; +import com.bartlomiejpluta.base.core.world.movement.MovableObject; +import org.joml.Vector2i; + +import java.util.List; + +public class ObjectLayer implements Layer { + private final Animator animator; + private final List objects; + + private final PassageAbility[][] passageMap; + + public ObjectLayer(Animator animator, List objects, PassageAbility[][] passageMap) { + this.animator = animator; + this.objects = objects; + this.passageMap = passageMap; + } + + public void addObject(MovableObject object) { + objects.add(object); + } + + public void removeObject(MovableObject object) { + objects.remove(object); + } + + public void setPassageAbility(int row, int column, PassageAbility passageAbility) { + passageMap[row][column] = passageAbility; + } + + public boolean isMovementPossible(Vector2i source, Vector2i target, Direction direction) { + var isTargetReachable = switch (passageMap[target.y][target.x]) { + case UP_ONLY -> direction != Direction.DOWN; + case DOWN_ONLY -> direction != Direction.UP; + case LEFT_ONLY -> direction != Direction.RIGHT; + case RIGHT_ONLY -> direction != Direction.LEFT; + case BLOCK -> false; + case ALLOW -> true; + }; + + var canMoveFromCurrentTile = switch (passageMap[source.y][source.x]) { + case UP_ONLY -> direction == Direction.UP; + case DOWN_ONLY -> direction == Direction.DOWN; + case LEFT_ONLY -> direction == Direction.LEFT; + case RIGHT_ONLY -> direction == Direction.RIGHT; + default -> true; + }; + + return isTargetReachable && canMoveFromCurrentTile; + } + + @Override + public void render(Window window, ShaderManager shaderManager) { + for (var object : objects) { + shaderManager.setUniform(UniformName.UNI_MODEL_MATRIX, object.getModelMatrix()); + animator.animate(object); + object.render(window, shaderManager); + } + } + + @Override + public void update(float dt) { + for (var object : objects) { + object.update(dt); + } + } +} diff --git a/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/TileLayer.java b/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/TileLayer.java new file mode 100755 index 00000000..1ee051c4 --- /dev/null +++ b/engine/src/main/java/com/bartlomiejpluta/base/core/world/map/TileLayer.java @@ -0,0 +1,44 @@ +package com.bartlomiejpluta.base.core.world.map; + +import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName; +import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager; +import com.bartlomiejpluta.base.core.ui.Window; +import com.bartlomiejpluta.base.core.world.tileset.model.Tile; +import org.joml.Vector2f; + +public class TileLayer implements Layer { + private final Tile[][] layer; + private final Vector2f stepSize; + private final float scale; + + public TileLayer(Tile[][] layer, Vector2f stepSize, float scale) { + this.layer = layer; + this.stepSize = stepSize; + this.scale = scale; + } + + public void setTile(int row, int column, Tile tile) { + layer[row][column] = tile; + recalculateTileGeometry(tile, row, column); + } + + private void recalculateTileGeometry(Tile tile, int row, int column) { + tile.setScale(scale); + tile.setPosition(column * stepSize.x, row * stepSize.y); + } + + @Override + public void render(Window window, ShaderManager shaderManager) { + for(var row : layer) { + for(var tile : row) { + shaderManager.setUniform(UniformName.UNI_MODEL_MATRIX, tile.getModelMatrix()); + tile.render(window, shaderManager); + } + } + } + + @Override + public void update(float dt) { + + } +} diff --git a/engine/src/main/java/com/bartlomiejpluta/base/core/world/object/RenderableObject.java b/engine/src/main/java/com/bartlomiejpluta/base/core/world/object/RenderableObject.java index 48a86188..2c49cfcc 100755 --- a/engine/src/main/java/com/bartlomiejpluta/base/core/world/object/RenderableObject.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/core/world/object/RenderableObject.java @@ -3,6 +3,7 @@ package com.bartlomiejpluta.base.core.world.object; import com.bartlomiejpluta.base.core.gl.object.material.Material; import com.bartlomiejpluta.base.core.gl.object.mesh.Mesh; import com.bartlomiejpluta.base.core.gl.render.Renderable; +import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName; import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager; import com.bartlomiejpluta.base.core.ui.Window; import lombok.EqualsAndHashCode; @@ -23,7 +24,14 @@ public abstract class RenderableObject extends PositionableObject implements Ren @Override public void render(Window window, ShaderManager shaderManager) { - getMaterial().activateTextureIfExists(); + material.activateTextureIfExists(); + + shaderManager.setUniform(UniformName.UNI_OBJECT_COLOR, material.getColor()); + shaderManager.setUniform(UniformName.UNI_HAS_OBJECT_TEXTURE, material.hasTexture()); + shaderManager.setUniform(UniformName.UNI_TEXTURE_SAMPLER, 0); + shaderManager.setUniform(UniformName.UNI_SPRITE_SIZE, material.getSpriteSize()); + shaderManager.setUniform(UniformName.UNI_SPRITE_POSITION, material.getSpritePosition()); + mesh.render(window, shaderManager); } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/core/world/scene/Scene.java b/engine/src/main/java/com/bartlomiejpluta/base/core/world/scene/Scene.java deleted file mode 100755 index ab92b7cf..00000000 --- a/engine/src/main/java/com/bartlomiejpluta/base/core/world/scene/Scene.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.bartlomiejpluta.base.core.world.scene; - -import com.bartlomiejpluta.base.core.gl.render.Renderable; -import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName; -import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager; -import com.bartlomiejpluta.base.core.logic.Updatable; -import com.bartlomiejpluta.base.core.ui.Window; -import com.bartlomiejpluta.base.core.world.animation.AnimationableObject; -import com.bartlomiejpluta.base.core.world.animation.Animator; -import com.bartlomiejpluta.base.core.world.camera.Camera; -import com.bartlomiejpluta.base.core.world.map.GameMap; -import com.bartlomiejpluta.base.core.world.movement.MovableObject; -import com.bartlomiejpluta.base.core.world.object.RenderableObject; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -import java.util.ArrayList; -import java.util.List; - -@AllArgsConstructor -public class Scene implements Renderable, Updatable { - private final Animator animator; - private final Camera camera; - private final List objects = new ArrayList<>(); - - @Setter - @Getter - private GameMap map; - - public Scene addObject(MovableObject object) { - objects.add(object); - return this; - } - - public Scene removeObject(MovableObject object) { - objects.remove(object); - return this; - } - - @Override - public void render(Window window, ShaderManager shaderManager) { - shaderManager.setUniform(UniformName.UNI_PROJECTION_MATRIX, camera.getProjectionMatrix(window)); - shaderManager.setUniform(UniformName.UNI_VIEW_MATRIX, camera.getViewMatrix()); - - renderArray(map.getLayer(0), window, shaderManager); - renderArray(map.getLayer(1), window, shaderManager); - - // Player will be rendered here - renderList(objects, window, shaderManager); - animator.animate(objects); - - renderArray(map.getLayer(2), window, shaderManager); - renderArray(map.getLayer(3), window, shaderManager); - } - - @Override - public void update(float dt) { - for(var object : objects) { - object.update(dt); - } - } - - private void renderList(List objects, Window window, ShaderManager shaderManager) { - for (var object : objects) { - if (object != null) { - renderObject(object, window, shaderManager); - } - } - } - - private void renderArray(T[] objects, Window window, ShaderManager shaderManager) { - for (var object : objects) { - if (object != null) { - renderObject(object, window, shaderManager); - } - } - } - - private void renderObject(T object, Window window, ShaderManager shaderManager) { - shaderManager.setUniform(UniformName.UNI_MODEL_MATRIX, object.getModelMatrix()); - shaderManager.setUniform(UniformName.UNI_OBJECT_COLOR, object.getMaterial().getColor()); - shaderManager.setUniform(UniformName.UNI_HAS_OBJECT_TEXTURE, object.getMaterial().hasTexture()); - shaderManager.setUniform(UniformName.UNI_TEXTURE_SAMPLER, 0); - shaderManager.setUniform(UniformName.UNI_SPRITE_SIZE, object.getMaterial().getSpriteSize()); - shaderManager.setUniform(UniformName.UNI_SPRITE_POSITION, object.getMaterial().getSpritePosition()); - - object.render(window, shaderManager); - } -} diff --git a/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/manager/DefaultEntityManager.java b/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/manager/DefaultEntityManager.java index 805c2b90..6cb560e1 100755 --- a/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/manager/DefaultEntityManager.java +++ b/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/manager/DefaultEntityManager.java @@ -3,7 +3,7 @@ package com.bartlomiejpluta.base.game.world.entity.manager; import com.bartlomiejpluta.base.core.gl.object.material.Material; import com.bartlomiejpluta.base.core.gl.object.mesh.Mesh; import com.bartlomiejpluta.base.core.util.mesh.MeshManager; -import com.bartlomiejpluta.base.core.world.scene.Scene; +import com.bartlomiejpluta.base.core.world.map.GameMap; import com.bartlomiejpluta.base.game.world.entity.config.EntitySpriteConfiguration; import com.bartlomiejpluta.base.game.world.entity.model.Entity; import lombok.RequiredArgsConstructor; @@ -19,8 +19,8 @@ public class DefaultEntityManager implements EntityManager { private final EntitySpriteConfiguration configuration; @Override - public Entity createEntity(Material material, Scene scene) { - return new Entity(scene, buildMesh(material), material, scene.getMap().getStepSize(), configuration); + public Entity createEntity(Material material, GameMap map) { + return new Entity(buildMesh(material), material, map.getStepSize(), configuration); } private Mesh buildMesh(Material material) { diff --git a/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/manager/EntityManager.java b/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/manager/EntityManager.java index 5f5f9d4d..f804867b 100755 --- a/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/manager/EntityManager.java +++ b/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/manager/EntityManager.java @@ -2,10 +2,9 @@ package com.bartlomiejpluta.base.game.world.entity.manager; import com.bartlomiejpluta.base.core.gc.Cleanable; import com.bartlomiejpluta.base.core.gl.object.material.Material; -import com.bartlomiejpluta.base.core.world.scene.Scene; +import com.bartlomiejpluta.base.core.world.map.GameMap; import com.bartlomiejpluta.base.game.world.entity.model.Entity; -import org.joml.Vector2f; public interface EntityManager extends Cleanable { - Entity createEntity(Material material, Scene scene); + Entity createEntity(Material material, GameMap map); } diff --git a/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/model/Entity.java b/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/model/Entity.java index 6c36df4e..9cdacafd 100755 --- a/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/model/Entity.java +++ b/game/src/main/java/com/bartlomiejpluta/base/game/world/entity/model/Entity.java @@ -4,7 +4,6 @@ import com.bartlomiejpluta.base.core.gl.object.material.Material; import com.bartlomiejpluta.base.core.gl.object.mesh.Mesh; import com.bartlomiejpluta.base.core.world.movement.Direction; import com.bartlomiejpluta.base.core.world.movement.MovableObject; -import com.bartlomiejpluta.base.core.world.scene.Scene; import com.bartlomiejpluta.base.game.world.entity.config.EntitySpriteConfiguration; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -13,14 +12,10 @@ import org.joml.Vector2f; import java.util.Map; -@EqualsAndHashCode(exclude = "scene", callSuper = true) +@EqualsAndHashCode(callSuper = true) public class Entity extends MovableObject { private final Map spriteDirectionRows; private final int defaultSpriteColumn; - private final Scene scene; - - @Getter - private boolean onScene; @Setter private int animationSpeed = 100; @@ -75,26 +70,10 @@ public class Entity extends MovableObject { return framesToCrossOneTile; } - public void pushToScene() { - if(!onScene) { - scene.addObject(this); - onScene = true; - } - } - - public void removeFromScene() { - if(onScene) { - scene.removeObject(this); - onScene = false; - } - } - - public Entity(Scene scene, Mesh mesh, Material material, Vector2f coordinateStepSize, EntitySpriteConfiguration configuration) { + public Entity(Mesh mesh, Material material, Vector2f coordinateStepSize, EntitySpriteConfiguration configuration) { super(mesh, material, coordinateStepSize, configuration.getDimension().asVector()); - this.scene = scene; this.defaultSpriteColumn = configuration.getDefaultSpriteColumn(); this.spriteDirectionRows = configuration.getSpriteDirectionRows(); - this.onScene = false; this.faceDirection = Direction.DOWN; } }