From 2e2ec772fab7a161551eedd7db1b055f316141fb Mon Sep 17 00:00:00 2001 From: Almas Baim Date: Thu, 21 Mar 2024 11:53:18 +0000 Subject: [PATCH] update pathfinding API to use any AStarCell derivative --- .../fxgl/core/collection/grid/Dungeon.java | 98 ----------------- .../core/collection/grid/DungeonCell.java | 24 ----- .../fxgl/pathfinding/astar/AStarGrid.java | 14 +-- .../pathfinding/astar/AStarMoveComponent.java | 18 ++-- .../pathfinding/astar/AStarPathfinder.java | 43 ++++---- .../pathfinding/astar/TraversableGrid.java | 43 ++++++++ .../fxgl/pathfinding/dungeon/DungeonCell.java | 16 +++ .../fxgl/pathfinding/dungeon/DungeonGrid.java | 100 ++++++++++++++++++ fxgl-entity/src/main/java/module-info.java | 1 + .../astar/AStarMoveComponentTest.kt | 2 +- .../pathfinding/astar/AStarPathfinderTest.kt | 4 +- .../main/java/sandbox/DungeonGenSample.java | 99 +++++++++++++++++ .../sandbox/ai/pathfinding/SenseAISample.java | 3 +- .../components/RandomAStarMoveComponent.kt | 4 +- 14 files changed, 301 insertions(+), 168 deletions(-) delete mode 100644 fxgl-core/src/main/java/com/almasb/fxgl/core/collection/grid/Dungeon.java delete mode 100644 fxgl-core/src/main/java/com/almasb/fxgl/core/collection/grid/DungeonCell.java create mode 100644 fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/TraversableGrid.java create mode 100644 fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/dungeon/DungeonCell.java create mode 100644 fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/dungeon/DungeonGrid.java create mode 100644 fxgl-samples/src/main/java/sandbox/DungeonGenSample.java diff --git a/fxgl-core/src/main/java/com/almasb/fxgl/core/collection/grid/Dungeon.java b/fxgl-core/src/main/java/com/almasb/fxgl/core/collection/grid/Dungeon.java deleted file mode 100644 index 99aa4c9761..0000000000 --- a/fxgl-core/src/main/java/com/almasb/fxgl/core/collection/grid/Dungeon.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * FXGL - JavaFX Game Library. The MIT License (MIT). - * Copyright (c) AlmasB (almaslvl@gmail.com). - * See LICENSE for details. - */ - -package com.almasb.fxgl.core.collection.grid; - -import java.util.Random; -import static java.lang.Math.abs; -import static java.lang.Math.sqrt; - - -public class Dungeon { - - // Create tilemap of given height and width - private DungeonCell[][] tileMap; - - private int[][] roomPositions = new int[20][2]; - - public void GenerateDungeon(int dungeonWidth, int dungeonHeight){ - - Random rand = new Random(); - tileMap = new DungeonCell[dungeonWidth][dungeonHeight]; - - // Setup empty dungeon - for (int i = 0; i < tileMap.length; i++){ - for (int j = 0; j < tileMap[0].length; j++){ - tileMap[i][j] = new DungeonCell(i, j); - } - } - - // Pick initial room positions - for (int i = 0; i < roomPositions.length; i++){ - roomPositions[i][0] = rand.nextInt(dungeonWidth); - roomPositions[i][1] = rand.nextInt(dungeonHeight); - } - - // Clear out rooms - for (int i = 0; i < roomPositions.length; i++){ - int subRooms = rand.nextInt(1) + 1; - - // Clear Circle - if (rand.nextInt(3) == 0) { - ClearCircle(roomPositions[i][0], roomPositions[i][1], rand.nextInt(3) + 1); - } - - // Clear Rect - else { - ClearRect(roomPositions[i][0], roomPositions[i][1], rand.nextInt(3) + 4, rand.nextInt(3) + 4); - } - - // Connect Rooms - int connectRoom = rand.nextInt(roomPositions.length); - - ClearPath(roomPositions[i][0], roomPositions[i][1], roomPositions[connectRoom][0], roomPositions[connectRoom][1]); - - } - } - - void ClearRect(int xPos, int yPos, int width, int height){ - for (int i = 0; i < tileMap.length; i++){ - for (int j = 0; j < tileMap[0].length; j++){ - int xDis = abs(i - xPos); - int yDis = abs(j - yPos); - - if (xDis <= width/2 && yDis <= height/2) { tileMap[i][j].SetType(0); } - } - } - } - - void ClearCircle(int xPos, int yPos, int radius){ - for (int i = 0; i < tileMap.length; i++){ - for (int j = 0; j < tileMap[0].length; j++){ - int xDis = abs(i - xPos); - int yDis = abs(j - yPos); - - double tileDis = sqrt(xDis*xDis + yDis*yDis); - if (tileDis <= radius) { tileMap[i][j].SetType(0); } - } - } - } - - void ClearPath(int xStart, int yStart, int xEnd, int yEnd){ - int[] clearPos = new int[2]; - clearPos[0] = xStart; - clearPos[1] = yStart; - - while (clearPos[0] != xEnd || clearPos[1] != yEnd){ - if (clearPos[0] < xEnd) clearPos[0]++; - else if (clearPos[0] > xEnd) clearPos[0]--; - else if (clearPos[1] < yEnd) clearPos[1]++; - else if (clearPos[1] > yEnd) clearPos[1]--; - - tileMap[clearPos[0]][clearPos[1]].SetType(0); - } - } -} \ No newline at end of file diff --git a/fxgl-core/src/main/java/com/almasb/fxgl/core/collection/grid/DungeonCell.java b/fxgl-core/src/main/java/com/almasb/fxgl/core/collection/grid/DungeonCell.java deleted file mode 100644 index 566f59e9d6..0000000000 --- a/fxgl-core/src/main/java/com/almasb/fxgl/core/collection/grid/DungeonCell.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * FXGL - JavaFX Game Library. The MIT License (MIT). - * Copyright (c) AlmasB (almaslvl@gmail.com). - * See LICENSE for details. - */ - -package com.almasb.fxgl.core.collection.grid; - -public class DungeonCell extends Cell { - private int cellType; - - public DungeonCell(int x, int y) { - super(x, y); - cellType = 1; - } - - public void SetType(int type){ - cellType = type; - } - - public int GetType(){ - return cellType; - } -} \ No newline at end of file diff --git a/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarGrid.java b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarGrid.java index 4fc0631d03..137e689844 100644 --- a/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarGrid.java +++ b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarGrid.java @@ -6,7 +6,6 @@ package com.almasb.fxgl.pathfinding.astar; -import com.almasb.fxgl.core.collection.grid.Grid; import com.almasb.fxgl.entity.Entity; import com.almasb.fxgl.entity.GameWorld; import com.almasb.fxgl.pathfinding.CellState; @@ -17,9 +16,13 @@ import java.util.stream.Collectors; /** + * Default implementation of the TraversableGrid for pathfinding purposes. + * Domain-specific grids, or those that require extra functionality, should extend + * TraversableGrid directly. + * * @author Almas Baimagambetov (AlmasB) (almaslvl@gmail.com) */ -public class AStarGrid extends Grid { +public final class AStarGrid extends TraversableGrid { /** * Constructs A* grid with A* cells using given width and height. @@ -29,13 +32,6 @@ public AStarGrid(int width, int height) { super(AStarCell.class, width, height, (x, y) -> new AStarCell(x, y, CellState.WALKABLE)); } - public List getWalkableCells() { - return getCells() - .stream() - .filter(c -> c.getState().isWalkable()) - .collect(Collectors.toList()); - } - /** * Generates an A* grid from the given world data. * diff --git a/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarMoveComponent.java b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarMoveComponent.java index c33de79647..833a91d2d1 100644 --- a/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarMoveComponent.java +++ b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarMoveComponent.java @@ -25,13 +25,13 @@ * @author Almas Baimagambetov (almaslvl@gmail.com) */ @Required(CellMoveComponent.class) -public final class AStarMoveComponent extends Component { +public final class AStarMoveComponent extends Component { private CellMoveComponent moveComponent; - private LazyValue pathfinder; + private LazyValue> pathfinder; - private List path = new ArrayList<>(); + private List path = new ArrayList<>(); private Runnable delayedPathCalc = EmptyRunnable.INSTANCE; @@ -44,21 +44,21 @@ public final class AStarMoveComponent extends Component { } }; - public AStarMoveComponent(AStarGrid grid) { + public AStarMoveComponent(TraversableGrid grid) { this(new LazyValue<>(() -> grid)); } /** * This ctor is for cases when the grid has not been constructed yet. */ - public AStarMoveComponent(LazyValue grid) { - pathfinder = new LazyValue<>(() -> new AStarPathfinder(grid.get())); + public AStarMoveComponent(LazyValue> grid) { + pathfinder = new LazyValue<>(() -> new AStarPathfinder<>(grid.get())); } /** * This ctor is for cases when using a pre-built pathfinder. */ - public AStarMoveComponent(AStarPathfinder pathfinderValue) { + public AStarMoveComponent(AStarPathfinder pathfinderValue) { pathfinder = new LazyValue<>(() -> pathfinderValue); } @@ -93,7 +93,7 @@ public boolean isAtDestination() { return isAtDestinationProp.get(); } - public AStarGrid getGrid() { + public TraversableGrid getGrid() { return pathfinder.get().getGrid(); } @@ -104,7 +104,7 @@ public AStarGrid getGrid() { * * @return cell where this entity is located */ - public Optional getCurrentCell() { + public Optional getCurrentCell() { var cellX = moveComponent.getCellX(); var cellY = moveComponent.getCellY(); diff --git a/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarPathfinder.java b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarPathfinder.java index a58aa13803..7c68147650 100644 --- a/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarPathfinder.java +++ b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarPathfinder.java @@ -21,27 +21,27 @@ /** * @author Almas Baimagambetov (almaslvl@gmail.com) */ -public final class AStarPathfinder implements Pathfinder { +public final class AStarPathfinder implements Pathfinder { - private final AStarGrid grid; + private final TraversableGrid grid; - private final Heuristic defaultHeuristic; - private final DiagonalHeuristic diagonalHeuristic; + private final Heuristic defaultHeuristic; + private final DiagonalHeuristic diagonalHeuristic; private boolean isCachingPaths = false; - private Map> cache = new HashMap<>(); + private Map> cache = new HashMap<>(); - public AStarPathfinder(AStarGrid grid) { + public AStarPathfinder(TraversableGrid grid) { this(grid, new ManhattanDistance<>(), new OctileDistance<>()); } - public AStarPathfinder(AStarGrid grid, Heuristic defaultHeuristic, DiagonalHeuristic diagonalHeuristic) { + public AStarPathfinder(TraversableGrid grid, Heuristic defaultHeuristic, DiagonalHeuristic diagonalHeuristic) { this.grid = grid; this.defaultHeuristic = defaultHeuristic; this.diagonalHeuristic = diagonalHeuristic; } - public AStarGrid getGrid() { + public TraversableGrid getGrid() { return grid; } @@ -58,22 +58,22 @@ public boolean isCachingPaths() { } @Override - public List findPath(int sourceX, int sourceY, int targetX, int targetY) { + public List findPath(int sourceX, int sourceY, int targetX, int targetY) { return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY)); } @Override - public List findPath(int sourceX, int sourceY, int targetX, int targetY, NeighborDirection neighborDirection) { + public List findPath(int sourceX, int sourceY, int targetX, int targetY, NeighborDirection neighborDirection) { return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY), neighborDirection); } @Override - public List findPath(int sourceX, int sourceY, int targetX, int targetY, List busyCells) { - return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY), busyCells.toArray(new AStarCell[0])); + public List findPath(int sourceX, int sourceY, int targetX, int targetY, List busyCells) { + return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY), NeighborDirection.FOUR_DIRECTIONS, busyCells.toArray(new AStarCell[0])); } @Override - public List findPath(int sourceX, int sourceY, int targetX, int targetY, NeighborDirection neighborDirection, List busyCells) { + public List findPath(int sourceX, int sourceY, int targetX, int targetY, NeighborDirection neighborDirection, List busyCells) { return findPath(grid.getData(), grid.get(sourceX, sourceY), grid.get(targetX, targetY), neighborDirection, busyCells.toArray(new AStarCell[0])); } @@ -87,7 +87,7 @@ public List findPath(int sourceX, int sourceY, int targetX, int targe * @param busyNodes busy "unwalkable" nodes * @return path as list of nodes from start (excl) to target (incl) or empty list if no path found */ - public List findPath(AStarCell[][] grid, AStarCell start, AStarCell target, AStarCell... busyNodes) { + public List findPath(T[][] grid, T start, T target, T... busyNodes) { return findPath(grid, start, target, NeighborDirection.FOUR_DIRECTIONS, busyNodes); } @@ -101,7 +101,7 @@ public List findPath(AStarCell[][] grid, AStarCell start, AStarCell t * @param busyNodes busy "unwalkable" nodes * @return path as list of nodes from start (excl) to target (incl) or empty list if no path found */ - public List findPath(AStarCell[][] grid, AStarCell start, AStarCell target, NeighborDirection neighborDirection, AStarCell... busyNodes) { + public List findPath(T[][] grid, T start, T target, NeighborDirection neighborDirection, AStarCell... busyNodes) { if (start == target || target.getState() == CellState.NOT_WALKABLE) return Collections.emptyList(); @@ -115,7 +115,7 @@ public List findPath(AStarCell[][] grid, AStarCell start, AStarCell t } } - Heuristic heuristic = (neighborDirection == FOUR_DIRECTIONS) ? defaultHeuristic : diagonalHeuristic; + Heuristic heuristic = (neighborDirection == FOUR_DIRECTIONS) ? defaultHeuristic : diagonalHeuristic; // reset grid cells data for (int y = 0; y < grid[0].length; y++) { @@ -195,13 +195,13 @@ public List findPath(AStarCell[][] grid, AStarCell start, AStarCell t return new ArrayList<>(path); } - private List buildPath(AStarCell start, AStarCell target) { - List path = new ArrayList<>(); + private List buildPath(T start, T target) { + List path = new ArrayList<>(); - AStarCell tmp = target; + T tmp = target; do { path.add(tmp); - tmp = tmp.getParent(); + tmp = (T) tmp.getParent(); } while (tmp != start); Collections.reverse(path); @@ -213,7 +213,7 @@ private List buildPath(AStarCell start, AStarCell target) { * @param busyNodes nodes which are busy, i.e. walkable but have a temporary obstacle * @return neighbors of the node */ - private List getValidNeighbors(AStarCell node, NeighborDirection neighborDirection, AStarCell... busyNodes) { + private List getValidNeighbors(AStarCell node, NeighborDirection neighborDirection, AStarCell... busyNodes) { var result = grid.getNeighbors(node.getX(), node.getY(), neighborDirection); result.removeAll(Arrays.asList(busyNodes)); result.removeIf(cell -> !cell.isWalkable()); @@ -223,5 +223,4 @@ private List getValidNeighbors(AStarCell node, NeighborDirection neig private boolean isDiagonal(Cell current, Cell neighbor) { return neighbor.getX() - current.getX() != 0 && neighbor.getY() - current.getY() != 0; } - } diff --git a/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/TraversableGrid.java b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/TraversableGrid.java new file mode 100644 index 0000000000..d717dbfde5 --- /dev/null +++ b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/TraversableGrid.java @@ -0,0 +1,43 @@ +/* + * FXGL - JavaFX Game Library. The MIT License (MIT). + * Copyright (c) AlmasB (almaslvl@gmail.com). + * See LICENSE for details. + */ + +package com.almasb.fxgl.pathfinding.astar; + +import com.almasb.fxgl.core.collection.grid.CellGenerator; +import com.almasb.fxgl.core.collection.grid.Grid; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * The supertype for any grid that can be traversed using A* pathfinding. + * + * @author Almas Baim (https://github.com/AlmasB) + */ +public abstract class TraversableGrid extends Grid { + + public TraversableGrid(Class type, int width, int height) { + super(type, width, height); + } + + public TraversableGrid(Class type, int width, int height, CellGenerator populateFunction) { + super(type, width, height, populateFunction); + } + + public TraversableGrid(Class type, int width, int height, int cellWidth, int cellHeight, CellGenerator populateFunction) { + super(type, width, height, cellWidth, cellHeight, populateFunction); + } + + /** + * @return all cells whose state is CellState.WALKABLE + */ + public List getWalkableCells() { + return getCells() + .stream() + .filter(c -> c.getState().isWalkable()) + .collect(Collectors.toList()); + } +} diff --git a/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/dungeon/DungeonCell.java b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/dungeon/DungeonCell.java new file mode 100644 index 0000000000..fa71a2ff19 --- /dev/null +++ b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/dungeon/DungeonCell.java @@ -0,0 +1,16 @@ +/* + * FXGL - JavaFX Game Library. The MIT License (MIT). + * Copyright (c) AlmasB (almaslvl@gmail.com). + * See LICENSE for details. + */ + +package com.almasb.fxgl.pathfinding.dungeon; + +import com.almasb.fxgl.pathfinding.CellState; +import com.almasb.fxgl.pathfinding.astar.AStarCell; + +public class DungeonCell extends AStarCell { + public DungeonCell(int x, int y) { + super(x, y, CellState.NOT_WALKABLE); + } +} \ No newline at end of file diff --git a/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/dungeon/DungeonGrid.java b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/dungeon/DungeonGrid.java new file mode 100644 index 0000000000..6d660dae7d --- /dev/null +++ b/fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/dungeon/DungeonGrid.java @@ -0,0 +1,100 @@ +/* + * FXGL - JavaFX Game Library. The MIT License (MIT). + * Copyright (c) AlmasB (almaslvl@gmail.com). + * See LICENSE for details. + */ + +package com.almasb.fxgl.pathfinding.dungeon; + +import com.almasb.fxgl.core.math.FXGLMath; +import com.almasb.fxgl.pathfinding.CellState; +import com.almasb.fxgl.pathfinding.astar.TraversableGrid; + +import java.util.Random; + +import static java.lang.Math.abs; +import static java.lang.Math.sqrt; + +public class DungeonGrid extends TraversableGrid { + + private DungeonCell[][] tileMap; + + private int[][] roomPositions = new int[20][2]; + + private Random random; + + public DungeonGrid(int width, int height) { + this(width, height, FXGLMath.getRandom()); + } + + public DungeonGrid(int width, int height, Random random) { + super(DungeonCell.class, width, height, DungeonCell::new); + tileMap = getData(); + this.random = random; + generateDungeon(); + } + + private void generateDungeon() { + // Pick initial room positions + for (int i = 0; i < roomPositions.length; i++) { + roomPositions[i][0] = random.nextInt(getWidth()); + roomPositions[i][1] = random.nextInt(getHeight()); + } + + // Clear out rooms + for (int i = 0; i < roomPositions.length; i++) { + if (random.nextInt(3) == 0) { + clearCircle(roomPositions[i][0], roomPositions[i][1], random.nextInt(3) + 1); + } else { + clearRect(roomPositions[i][0], roomPositions[i][1], random.nextInt(3) + 4, random.nextInt(3) + 4); + } + + // Connect Rooms + int connectRoom = random.nextInt(roomPositions.length); + + clearPath(roomPositions[i][0], roomPositions[i][1], roomPositions[connectRoom][0], roomPositions[connectRoom][1]); + } + } + + private void clearRect(int xPos, int yPos, int width, int height) { + for (int i = 0; i < tileMap.length; i++) { + for (int j = 0; j < tileMap[0].length; j++) { + int xDis = abs(i - xPos); + int yDis = abs(j - yPos); + + if (xDis <= width / 2 && yDis <= height / 2) { + tileMap[i][j].setState(CellState.WALKABLE); + } + } + } + } + + private void clearCircle(int xPos, int yPos, int radius) { + for (int i = 0; i < tileMap.length; i++) { + for (int j = 0; j < tileMap[0].length; j++) { + int xDis = abs(i - xPos); + int yDis = abs(j - yPos); + + double tileDis = sqrt(xDis * xDis + yDis * yDis); + if (tileDis <= radius) { + tileMap[i][j].setState(CellState.WALKABLE); + } + } + } + } + + private void clearPath(int xStart, int yStart, int xEnd, int yEnd) { + int[] clearPos = new int[2]; + clearPos[0] = xStart; + clearPos[1] = yStart; + + while (clearPos[0] != xEnd || clearPos[1] != yEnd) { + if (clearPos[0] < xEnd) clearPos[0]++; + else if (clearPos[0] > xEnd) clearPos[0]--; + else if (clearPos[1] < yEnd) clearPos[1]++; + else if (clearPos[1] > yEnd) clearPos[1]--; + + tileMap[clearPos[0]][clearPos[1]].setState(CellState.WALKABLE); + } + } +} \ No newline at end of file diff --git a/fxgl-entity/src/main/java/module-info.java b/fxgl-entity/src/main/java/module-info.java index 694a7981a6..bb1b139727 100644 --- a/fxgl-entity/src/main/java/module-info.java +++ b/fxgl-entity/src/main/java/module-info.java @@ -23,6 +23,7 @@ exports com.almasb.fxgl.particle; exports com.almasb.fxgl.pathfinding; exports com.almasb.fxgl.pathfinding.astar; + exports com.almasb.fxgl.pathfinding.dungeon; exports com.almasb.fxgl.pathfinding.heuristic; exports com.almasb.fxgl.pathfinding.maze; exports com.almasb.fxgl.physics; diff --git a/fxgl-entity/src/test/kotlin/com/almasb/fxgl/pathfinding/astar/AStarMoveComponentTest.kt b/fxgl-entity/src/test/kotlin/com/almasb/fxgl/pathfinding/astar/AStarMoveComponentTest.kt index f9974471b3..e9ab2e33c1 100644 --- a/fxgl-entity/src/test/kotlin/com/almasb/fxgl/pathfinding/astar/AStarMoveComponentTest.kt +++ b/fxgl-entity/src/test/kotlin/com/almasb/fxgl/pathfinding/astar/AStarMoveComponentTest.kt @@ -21,7 +21,7 @@ class AStarMoveComponentTest { private lateinit var e: Entity private lateinit var grid: AStarGrid - private lateinit var aStarMoveComponent: AStarMoveComponent + private lateinit var aStarMoveComponent: AStarMoveComponent private lateinit var cellMoveComponent: CellMoveComponent @BeforeEach diff --git a/fxgl-entity/src/test/kotlin/com/almasb/fxgl/pathfinding/astar/AStarPathfinderTest.kt b/fxgl-entity/src/test/kotlin/com/almasb/fxgl/pathfinding/astar/AStarPathfinderTest.kt index 11c1c6154b..cb50cc9f62 100644 --- a/fxgl-entity/src/test/kotlin/com/almasb/fxgl/pathfinding/astar/AStarPathfinderTest.kt +++ b/fxgl-entity/src/test/kotlin/com/almasb/fxgl/pathfinding/astar/AStarPathfinderTest.kt @@ -19,8 +19,8 @@ import java.util.function.Supplier class AStarPathfinderTest { private lateinit var grid: AStarGrid - private lateinit var pathfinder: AStarPathfinder - private lateinit var pathfinderHeuristics: AStarPathfinder + private lateinit var pathfinder: AStarPathfinder + private lateinit var pathfinderHeuristics: AStarPathfinder @BeforeEach fun setUp() { diff --git a/fxgl-samples/src/main/java/sandbox/DungeonGenSample.java b/fxgl-samples/src/main/java/sandbox/DungeonGenSample.java new file mode 100644 index 0000000000..0e0e9a5c1a --- /dev/null +++ b/fxgl-samples/src/main/java/sandbox/DungeonGenSample.java @@ -0,0 +1,99 @@ +/* + * FXGL - JavaFX Game Library. The MIT License (MIT). + * Copyright (c) AlmasB (almaslvl@gmail.com). + * See LICENSE for details. + */ + +package sandbox; + +import com.almasb.fxgl.app.GameApplication; +import com.almasb.fxgl.app.GameSettings; +import com.almasb.fxgl.core.math.FXGLMath; +import com.almasb.fxgl.dsl.components.RandomAStarMoveComponent; +import com.almasb.fxgl.pathfinding.astar.AStarCell; +import com.almasb.fxgl.pathfinding.dungeon.DungeonGrid; +import com.almasb.fxgl.pathfinding.CellMoveComponent; +import com.almasb.fxgl.pathfinding.astar.AStarMoveComponent; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; +import javafx.util.Duration; + +import static com.almasb.fxgl.dsl.FXGL.*; + +/** + * @author Almas Baim (https://github.com/AlmasB) + */ +public class DungeonGenSample extends GameApplication { + @Override + protected void initSettings(GameSettings settings) { + settings.setWidth(1280); + settings.setHeight(720); + } + + @Override + protected void initGame() { + DungeonGrid dungeon = new DungeonGrid(130, 72); + + var scale = 10; + + var agent = entityBuilder() + .viewWithBBox(new Rectangle(scale, scale, Color.BLUE)) + .with(new CellMoveComponent(scale, scale, 150)) + .with(new AStarMoveComponent<>(dungeon)) + .zIndex(1) + .anchorFromCenter() + .buildAndAttach(); + + for (int y = 0; y < 72; y++) { + for (int x = 0; x < 130; x++) { + var finalX = x; + var finalY = y; + + var tile = dungeon.get(x, y); + + var rect = new Rectangle(scale, scale, Color.WHITE); + + if (!tile.isWalkable()) { + rect.setFill(Color.GRAY); + } else { + rect.setFill(Color.WHITE); + agent.getComponent(AStarMoveComponent.class).stopMovementAt(finalX, finalY); + + rect.setOnMouseClicked(e -> { + agent.getComponent(AStarMoveComponent.class).moveToCell(finalX, finalY); + }); + + if (FXGLMath.randomBoolean(0.09)) { + spawnNPC(x, y, dungeon); + } + } + + entityBuilder() + .at(x*scale, y*scale) + .view(rect) + .buildAndAttach(); + } + } + } + + private void spawnNPC(int x, int y, DungeonGrid grid) { + var view = new Rectangle(10, 10, FXGLMath.randomColor().brighter().brighter()); + view.setStroke(Color.BLACK); + view.setStrokeWidth(2); + + var e = entityBuilder() + .zIndex(2) + .viewWithBBox(view) + .anchorFromCenter() + .with(new CellMoveComponent(10, 10, 150)) + .with(new AStarMoveComponent<>(grid)) + .with(new RandomAStarMoveComponent(1, 7, Duration.seconds(1), Duration.seconds(3))) + .buildAndAttach(); + + e.getComponent(AStarMoveComponent.class).stopMovementAt(x, y); + } + + public static void main(String[] args) { + launch(args); + } +} diff --git a/fxgl-samples/src/main/java/sandbox/ai/pathfinding/SenseAISample.java b/fxgl-samples/src/main/java/sandbox/ai/pathfinding/SenseAISample.java index 2e50ebbe3b..394b42d930 100644 --- a/fxgl-samples/src/main/java/sandbox/ai/pathfinding/SenseAISample.java +++ b/fxgl-samples/src/main/java/sandbox/ai/pathfinding/SenseAISample.java @@ -18,6 +18,7 @@ import com.almasb.fxgl.entity.component.Component; import com.almasb.fxgl.pathfinding.CellMoveComponent; import com.almasb.fxgl.pathfinding.CellState; +import com.almasb.fxgl.pathfinding.astar.AStarCell; import com.almasb.fxgl.pathfinding.astar.AStarGrid; import com.almasb.fxgl.pathfinding.astar.AStarMoveComponent; import javafx.scene.input.MouseButton; @@ -113,7 +114,7 @@ private void spawnNPC(int x, int y) { private static class CustomAIComponent extends Component { private CellMoveComponent cellMove; - private AStarMoveComponent astarMove; + private AStarMoveComponent astarMove; private HearingSenseComponent sense; @Override diff --git a/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/components/RandomAStarMoveComponent.kt b/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/components/RandomAStarMoveComponent.kt index bb800d9c72..f16c116d36 100644 --- a/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/components/RandomAStarMoveComponent.kt +++ b/fxgl/src/main/kotlin/com/almasb/fxgl/dsl/components/RandomAStarMoveComponent.kt @@ -21,7 +21,7 @@ import java.util.function.Predicate * @author Almas Baimagambetov (almaslvl@gmail.com) */ @Required(AStarMoveComponent::class) -class RandomAStarMoveComponent +class RandomAStarMoveComponent @JvmOverloads constructor( /** @@ -50,7 +50,7 @@ class RandomAStarMoveComponent ) : Component() { - private lateinit var astar: AStarMoveComponent + private lateinit var astar: AStarMoveComponent private val moveTimer = FXGL.newLocalTimer()