From 3d167ce18382b35ecb8500b8e96765635201d869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Tue, 30 Aug 2022 11:16:45 +0200 Subject: [PATCH] Add support for 2x2 autotiles | create connect auto tile option #3 --- .../serial/ProtobufProjectDeserializer.java | 3 +- .../autotile/asset/AutoTileSetAsset.java | 6 +- .../manager/DefaultAutoTileSetManager.java | 4 +- .../world/autotile/model/AutoTileLayout.java | 14 ++- .../world/autotile/model/AutoTileSet.java | 113 ++++++++++++++---- .../layer/autotile/DefaultAutoTileLayer.java | 76 ++++++++++-- .../world/map/model/DefaultGameMap.java | 4 +- .../map/serial/ProtobufMapDeserializer.java | 3 +- 8 files changed, 183 insertions(+), 40 deletions(-) diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/project/serial/ProtobufProjectDeserializer.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/project/serial/ProtobufProjectDeserializer.java index c706ca1d..d74206f4 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/project/serial/ProtobufProjectDeserializer.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/project/serial/ProtobufProjectDeserializer.java @@ -6,6 +6,7 @@ import com.bartlomiejpluta.base.engine.gui.asset.WidgetDefinitionAsset; import com.bartlomiejpluta.base.engine.project.model.Project; import com.bartlomiejpluta.base.engine.world.animation.asset.AnimationAsset; import com.bartlomiejpluta.base.engine.world.autotile.asset.AutoTileSetAsset; +import com.bartlomiejpluta.base.engine.world.autotile.model.AutoTileLayout; import com.bartlomiejpluta.base.engine.world.character.asset.CharacterSetAsset; import com.bartlomiejpluta.base.engine.world.icon.asset.IconSetAsset; import com.bartlomiejpluta.base.engine.world.image.asset.ImageAsset; @@ -42,7 +43,7 @@ public class ProtobufProjectDeserializer extends ProjectDeserializer { } private AutoTileSetAsset parseAutoTileSetAsset(ProjectProto.AutoTileSetAsset proto) { - return new AutoTileSetAsset(proto.getUid(), proto.getSource(), proto.getRows(), proto.getColumns()); + return new AutoTileSetAsset(proto.getUid(), proto.getSource(), proto.getRows(), proto.getColumns(), AutoTileLayout.valueOf(proto.getLayout().name())); } private TileSetAsset parseTileSetAsset(ProjectProto.TileSetAsset proto) { diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/asset/AutoTileSetAsset.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/asset/AutoTileSetAsset.java index 4dfd4e93..db073895 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/asset/AutoTileSetAsset.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/asset/AutoTileSetAsset.java @@ -1,6 +1,7 @@ package com.bartlomiejpluta.base.engine.world.autotile.asset; import com.bartlomiejpluta.base.engine.common.asset.Asset; +import com.bartlomiejpluta.base.engine.world.autotile.model.AutoTileLayout; import lombok.Getter; import lombok.NonNull; @@ -8,10 +9,13 @@ import lombok.NonNull; public class AutoTileSetAsset extends Asset { private final int rows; private final int columns; + private final AutoTileLayout layout; - public AutoTileSetAsset(@NonNull String uid, @NonNull String source, int rows, int columns) { + + public AutoTileSetAsset(@NonNull String uid, @NonNull String source, int rows, int columns, @NonNull AutoTileLayout layout) { super(uid, source); this.rows = rows; this.columns = columns; + this.layout = layout; } } \ No newline at end of file diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/manager/DefaultAutoTileSetManager.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/manager/DefaultAutoTileSetManager.java index 1db96e03..a3b0be08 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/manager/DefaultAutoTileSetManager.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/manager/DefaultAutoTileSetManager.java @@ -54,8 +54,8 @@ public class DefaultAutoTileSetManager implements AutoTileManager { } var source = configuration.projectFile("autotiles", asset.getSource()); - var texture = textureManager.loadTexture(source, asset.getRows() * AutoTileSet.ROWS, asset.getColumns() * AutoTileSet.COLUMNS); - autoTile = new AutoTileSet(texture, mesh, asset.getRows(), asset.getColumns()); + var texture = textureManager.loadTexture(source, asset.getRows() * asset.getLayout().getRows() * 2, asset.getColumns() * asset.getLayout().getColumns() * 2); + autoTile = new AutoTileSet(texture, mesh, asset.getRows(), asset.getColumns(), asset.getLayout()); log.info("Loading auto tile set from assets to cache under the key: [{}]", uid); autoTiles.put(uid, autoTile); } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/model/AutoTileLayout.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/model/AutoTileLayout.java index 109402cf..9fc34928 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/model/AutoTileLayout.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/model/AutoTileLayout.java @@ -1,2 +1,14 @@ -package com.bartlomiejpluta.base.engine.world.autotile.model;public enum AutoTileLayout { +package com.bartlomiejpluta.base.engine.world.autotile.model; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum AutoTileLayout { + LAYOUT_2X2(2, 2), + LAYOUT_2X3(2, 3); + + private final int columns; + private final int rows; } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/model/AutoTileSet.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/model/AutoTileSet.java index 4f520420..5352d62d 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/model/AutoTileSet.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/autotile/model/AutoTileSet.java @@ -12,30 +12,36 @@ import java.util.LinkedList; // Algorithm source: https://love2d.org/forums/viewtopic.php?t=7826 public class AutoTileSet { - public static final int ROWS = 6; - public static final int COLUMNS = 4; + private static final Vector2ic ISLAND_TILE_2x3 = new Vector2i(0, 0); + private static final Vector2ic CROSS_TILE_2x3 = new Vector2i(1, 0); + private static final Vector2ic TOP_LEFT_TILE_2x3 = new Vector2i(0, 1); + private static final Vector2ic TOP_RIGHT_TILE_2x3 = new Vector2i(1, 1); + private static final Vector2ic BOTTOM_LEFT_TILE_2x3 = new Vector2i(0, 2); + private static final Vector2ic BOTTOM_RIGHT_TILE_2x3 = new Vector2i(1, 2); + + private static final Vector2ic TOP_LEFT_TILE_2x2 = new Vector2i(0, 0); + private static final Vector2ic TOP_RIGHT_TILE_2x2 = new Vector2i(1, 0); + private static final Vector2ic BOTTOM_LEFT_TILE_2x2 = new Vector2i(0, 1); + private static final Vector2ic BOTTOM_RIGHT_TILE_2x2 = new Vector2i(1, 1); + + private final int textureSubTilesRows; + private final int textureSubTilesColumns; - private static final Vector2ic ISLAND_TILE = new Vector2i(0, 0); - private static final Vector2ic CROSS_TILE = new Vector2i(1, 0); - private static final Vector2ic TOP_LEFT_TILE = new Vector2i(0, 1); - private static final Vector2ic TOP_RIGHT_TILE = new Vector2i(1, 1); - private static final Vector2ic BOTTOM_LEFT_TILE = new Vector2i(0, 2); - private static final Vector2ic BOTTOM_RIGHT_TILE = new Vector2i(1, 2); @Getter - private final Vector2ic[][] islandSubTiles; + private Vector2ic[][] islandSubTiles; @Getter - private final Vector2ic[][] topLeftSubTiles; + private Vector2ic[][] topLeftSubTiles; @Getter - private final Vector2ic[][] topRightSubTiles; + private Vector2ic[][] topRightSubTiles; @Getter - private final Vector2ic[][] bottomLeftSubTiles; + private Vector2ic[][] bottomLeftSubTiles; @Getter - private final Vector2ic[][] bottomRightSubTiles; + private Vector2ic[][] bottomRightSubTiles; @Getter private final Texture texture; @@ -43,20 +49,33 @@ public class AutoTileSet { @Getter private final Vector2fc tileSize; + + @Getter + private final AutoTileLayout layout; private final int rows; private final int columns; @Getter private final int setsCount; - public AutoTileSet(@NonNull Texture texture, @NonNull Mesh mesh, int rows, int columns) { + public AutoTileSet(@NonNull Texture texture, @NonNull Mesh mesh, int rows, int columns, @NonNull AutoTileLayout layout) { this.texture = texture; this.mesh = mesh; this.rows = rows; this.columns = columns; this.tileSize = texture.getSpriteSize(); + this.layout = layout; this.setsCount = rows * columns; + this.textureSubTilesRows = layout.getRows() * 2; + this.textureSubTilesColumns = layout.getColumns() * 2; + switch (layout) { + case LAYOUT_2X2 -> init2x2(); + case LAYOUT_2X3 -> init2x3(); + } + } + + private void init2x3() { var islandSubTiles = new LinkedList(); var topLeftSubTiles = new LinkedList(); var topRightSubTiles = new LinkedList(); @@ -64,11 +83,11 @@ public class AutoTileSet { var bottomRightSubTiles = new LinkedList(); for (int setId = 0; setId < setsCount; ++setId) { - var crossSubTiles = cutSubTiles(setId, CROSS_TILE); - var topLeftTileSubTiles = cutSubTiles(setId, TOP_LEFT_TILE); - var topRightTileSubTiles = cutSubTiles(setId, TOP_RIGHT_TILE); - var bottomLeftTileSubTiles = cutSubTiles(setId, BOTTOM_LEFT_TILE); - var bottomRightTileSubTiles = cutSubTiles(setId, BOTTOM_RIGHT_TILE); + var crossSubTiles = cutSubTiles(setId, CROSS_TILE_2x3); + var topLeftTileSubTiles = cutSubTiles(setId, TOP_LEFT_TILE_2x3); + var topRightTileSubTiles = cutSubTiles(setId, TOP_RIGHT_TILE_2x3); + var bottomLeftTileSubTiles = cutSubTiles(setId, BOTTOM_LEFT_TILE_2x3); + var bottomRightTileSubTiles = cutSubTiles(setId, BOTTOM_RIGHT_TILE_2x3); /* * Indexes: @@ -103,7 +122,7 @@ public class AutoTileSet { var bl2 = bottomRightTileSubTiles[2]; var br0 = bottomRightTileSubTiles[3]; - islandSubTiles.add(cutSubTiles(setId, ISLAND_TILE)); + islandSubTiles.add(cutSubTiles(setId, ISLAND_TILE_2x3)); topLeftSubTiles.add(new Vector2ic[]{tl0, tl1, tl2, tl3, tl4}); topRightSubTiles.add(new Vector2ic[]{tr0, tr1, tr2, tr3, tr4}); bottomLeftSubTiles.add(new Vector2ic[]{bl0, bl1, bl2, bl3, bl4}); @@ -117,6 +136,58 @@ public class AutoTileSet { this.bottomRightSubTiles = bottomRightSubTiles.toArray(new Vector2ic[bottomRightSubTiles.size()][]); } + private void init2x2() { + var topLeftSubTiles = new LinkedList(); + var topRightSubTiles = new LinkedList(); + var bottomLeftSubTiles = new LinkedList(); + var bottomRightSubTiles = new LinkedList(); + + for (int setId = 0; setId < setsCount; ++setId) { + var topLeftTileSubTiles = cutSubTiles(setId, TOP_LEFT_TILE_2x2); + var topRightTileSubTiles = cutSubTiles(setId, TOP_RIGHT_TILE_2x2); + var bottomLeftTileSubTiles = cutSubTiles(setId, BOTTOM_LEFT_TILE_2x2); + var bottomRightTileSubTiles = cutSubTiles(setId, BOTTOM_RIGHT_TILE_2x2); + + /* + * Indexes: + * 0 - No connected tiles + * 1 - Left tile is connected + * 2 - Right tile is connected + * 3 - Left, Right, and Center tiles are connected. + */ + + var tl0 = topLeftTileSubTiles[0]; + var tr2 = topLeftTileSubTiles[1]; + var bl1 = topLeftTileSubTiles[2]; + var br3 = topLeftTileSubTiles[3]; + + var tl1 = topRightTileSubTiles[0]; + var tr0 = topRightTileSubTiles[1]; + var bl3 = topRightTileSubTiles[2]; + var br2 = topRightTileSubTiles[3]; + + var tl2 = bottomLeftTileSubTiles[0]; + var tr3 = bottomLeftTileSubTiles[1]; + var bl0 = bottomLeftTileSubTiles[2]; + var br1 = bottomLeftTileSubTiles[3]; + + var tl3 = bottomRightTileSubTiles[0]; + var tr1 = bottomRightTileSubTiles[1]; + var bl2 = bottomRightTileSubTiles[2]; + var br0 = bottomRightTileSubTiles[3]; + + topLeftSubTiles.add(new Vector2ic[]{tl0, tl1, tl2, tl3}); + topRightSubTiles.add(new Vector2ic[]{tr0, tr1, tr2, tr3}); + bottomLeftSubTiles.add(new Vector2ic[]{bl0, bl1, bl2, bl3}); + bottomRightSubTiles.add(new Vector2ic[]{br0, br1, br2, br3}); + } + + this.topLeftSubTiles = topLeftSubTiles.toArray(new Vector2ic[topLeftSubTiles.size()][]); + this.topRightSubTiles = topRightSubTiles.toArray(new Vector2ic[topRightSubTiles.size()][]); + this.bottomLeftSubTiles = bottomLeftSubTiles.toArray(new Vector2ic[bottomLeftSubTiles.size()][]); + this.bottomRightSubTiles = bottomRightSubTiles.toArray(new Vector2ic[bottomRightSubTiles.size()][]); + } + private Vector2ic[] cutSubTiles(int setId, Vector2ic tile) { var topLeft = getTile(setId, tile.y() * 2, tile.x() * 2); var topRight = getTile(setId, tile.y() * 2, tile.x() * 2 + 1); @@ -127,7 +198,7 @@ public class AutoTileSet { } private Vector2ic getTile(int setId, int row, int column) { - return new Vector2i(((setId / columns) * ROWS) + row, ((setId % columns) * COLUMNS) + column); + return new Vector2i(((setId / columns) * textureSubTilesRows) + row, ((setId % columns) * textureSubTilesColumns) + column); } public AutoTile createTile(int setId) { diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/layer/autotile/DefaultAutoTileLayer.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/layer/autotile/DefaultAutoTileLayer.java index 564b9e5b..69e8046c 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/layer/autotile/DefaultAutoTileLayer.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/layer/autotile/DefaultAutoTileLayer.java @@ -16,15 +16,17 @@ import java.util.Arrays; public class DefaultAutoTileLayer extends BaseLayer implements AutoTileLayer { private final AutoTileSet autoTileSet; private final AutoTile[][] layer; - private boolean animated; - private double animationDuration; + private final boolean animated; + private final double animationDuration; + private final boolean connect; private double accumulator; - public DefaultAutoTileLayer(@NonNull GameMap map, @NonNull AutoTileSet autoTileSet, int rows, int columns, boolean animated, double animationDuration) { + public DefaultAutoTileLayer(@NonNull GameMap map, @NonNull AutoTileSet autoTileSet, int rows, int columns, boolean animated, double animationDuration, boolean connect) { super(map); this.autoTileSet = autoTileSet; this.animated = animated; this.animationDuration = animationDuration; + this.connect = connect; layer = new AutoTile[rows][columns]; Arrays.stream(layer).forEach(tiles -> Arrays.fill(tiles, null)); } @@ -48,6 +50,13 @@ public class DefaultAutoTileLayer extends BaseLayer implements AutoTileLayer { } private void recalculateTile(Integer setId, int row, int column) { + switch(autoTileSet.getLayout()) { + case LAYOUT_2X2 -> recalculateTile2x2(setId, row, column); + case LAYOUT_2X3 -> recalculateTile2x3(setId, row, column); + } + } + + private void recalculateTile2x2(Integer setId, int row, int column) { if (layer[row][column] == null) { return; } @@ -58,52 +67,97 @@ public class DefaultAutoTileLayer extends BaseLayer implements AutoTileLayer { var bottomRight = 0; var tile = layer[row][column]; + var centerSetId = tile.getSetId(); if (animated) { tile.shiftTileSet(); } // Top - if (row > 0 && layer[row - 1][column] != null) { + if (row > 0 && (layer[row - 1][column] != null && (connect || layer[row - 1][column].getSetId() == centerSetId))) { topLeft += 2; topRight += 1; } // Bottom - if (row < map.getRows() - 1 && layer[row + 1][column] != null) { + if (row < map.getRows() - 1 && (layer[row + 1][column] != null && (connect || layer[row + 1][column].getSetId() == centerSetId))) { bottomLeft += 1; bottomRight += 2; } // Left - if (column > 0 && layer[row][column - 1] != null) { + if (column > 0 && (layer[row][column - 1] != null && (connect || layer[row][column - 1].getSetId() == centerSetId))) { topLeft += 1; bottomLeft += 2; } // Right - if (column < map.getColumns() - 1 && layer[row][column + 1] != null) { + if (column < map.getColumns() - 1 && (layer[row][column + 1] != null && (connect || layer[row][column + 1].getSetId() == centerSetId))) { + topRight += 2; + bottomRight += 1; + } + + tile.regularTile(setId, topLeft, topRight, bottomLeft, bottomRight); + } + + private void recalculateTile2x3(Integer setId, int row, int column) { + if (layer[row][column] == null) { + return; + } + + var topLeft = 0; + var topRight = 0; + var bottomLeft = 0; + var bottomRight = 0; + + var tile = layer[row][column]; + var centerSetId = tile.getSetId(); + + if (animated) { + tile.shiftTileSet(); + } + + // Top + if (row > 0 && (layer[row - 1][column] != null && (connect || layer[row - 1][column].getSetId() == centerSetId))) { + topLeft += 2; + topRight += 1; + } + + // Bottom + if (row < map.getRows() - 1 && (layer[row + 1][column] != null && (connect || layer[row + 1][column].getSetId() == centerSetId))) { + bottomLeft += 1; + bottomRight += 2; + } + + // Left + if (column > 0 && (layer[row][column - 1] != null && (connect || layer[row][column - 1].getSetId() == centerSetId))) { + topLeft += 1; + bottomLeft += 2; + } + + // Right + if (column < map.getColumns() - 1 && (layer[row][column + 1] != null && (connect || layer[row][column + 1].getSetId() == centerSetId))) { topRight += 2; bottomRight += 1; } // Top left - if (row > 0 && column > 0 && layer[row - 1][column - 1] != null && topLeft == 3) { + if (row > 0 && column > 0 && (layer[row - 1][column - 1] != null && (connect || layer[row - 1][column - 1].getSetId() == centerSetId)) && topLeft == 3) { topLeft = 4; } // Top right - if (row > 0 && column < map.getColumns() - 1 && layer[row - 1][column + 1] != null && topRight == 3) { + if (row > 0 && column < map.getColumns() - 1 && (layer[row - 1][column + 1] != null && (connect || layer[row - 1][column + 1].getSetId() == centerSetId)) && topRight == 3) { topRight = 4; } // Bottom left - if (row < map.getRows() - 1 && column > 0 && layer[row + 1][column - 1] != null && bottomLeft == 3) { + if (row < map.getRows() - 1 && column > 0 && (layer[row + 1][column - 1] != null && (connect || layer[row + 1][column - 1].getSetId() == centerSetId)) && bottomLeft == 3) { bottomLeft = 4; } // Bottom right - if (row < map.getRows() - 1 && column < map.getColumns() - 1 && layer[row + 1][column + 1] != null && bottomRight == 3) { + if (row < map.getRows() - 1 && column < map.getColumns() - 1 && (layer[row + 1][column + 1] != null && (connect || layer[row + 1][column + 1].getSetId() == centerSetId)) && bottomRight == 3) { bottomRight = 4; } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/model/DefaultGameMap.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/model/DefaultGameMap.java index 110ca010..25261173 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/model/DefaultGameMap.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/model/DefaultGameMap.java @@ -120,8 +120,8 @@ public class DefaultGameMap implements Renderable, Updatable, GameMap { return layer; } - public DefaultAutoTileLayer createAutoTileLayer(@NonNull AutoTileSet autoTileSet, boolean animated, double animationDuration) { - var layer = new DefaultAutoTileLayer(this, autoTileSet, rows, columns, animated, animationDuration); + public DefaultAutoTileLayer createAutoTileLayer(@NonNull AutoTileSet autoTileSet, boolean animated, double animationDuration, boolean connect) { + var layer = new DefaultAutoTileLayer(this, autoTileSet, rows, columns, animated, animationDuration, connect); layers.add(layer); return layer; diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/serial/ProtobufMapDeserializer.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/serial/ProtobufMapDeserializer.java index b2300653..ba84138b 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/serial/ProtobufMapDeserializer.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/map/serial/ProtobufMapDeserializer.java @@ -71,8 +71,9 @@ public class ProtobufMapDeserializer extends MapDeserializer { var autoTileSet = autoTileManager.loadObject(proto.getAutoTileLayer().getAutotileUID()); var animated = proto.getAutoTileLayer().getAnimated(); var animationDuration = proto.getAutoTileLayer().getAnimationDuration(); + var connect = proto.getAutoTileLayer().getConnect(); - var layer = map.createAutoTileLayer(autoTileSet, animated, animationDuration); + var layer = map.createAutoTileLayer(autoTileSet, animated, animationDuration, connect); var columns = map.getColumns(); var tiles = proto.getAutoTileLayer().getTilesList();