Skip to content

Commit fac790b

Browse files
committed
feat: added atDestinationProperty() to AStarMoveComponent, closes #945
1 parent f41bc25 commit fac790b

File tree

4 files changed

+149
-96
lines changed

4 files changed

+149
-96
lines changed

fxgl-entity/src/main/java/com/almasb/fxgl/pathfinding/astar/AStarMoveComponent.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import com.almasb.fxgl.entity.component.Component;
1313
import com.almasb.fxgl.entity.component.Required;
1414
import com.almasb.fxgl.pathfinding.CellMoveComponent;
15+
import javafx.beans.property.ReadOnlyBooleanProperty;
16+
import javafx.beans.property.ReadOnlyBooleanWrapper;
1517
import javafx.beans.value.ChangeListener;
1618

1719
import java.util.ArrayList;
@@ -33,6 +35,8 @@ public final class AStarMoveComponent extends Component {
3335

3436
private Runnable delayedPathCalc = EmptyRunnable.INSTANCE;
3537

38+
private ReadOnlyBooleanWrapper isAtDestinationProp = new ReadOnlyBooleanWrapper(true);
39+
3640
private ChangeListener<Boolean> isAtDestinationListener = (o, old, isAtDestination) -> {
3741
if (isAtDestination) {
3842
delayedPathCalc.run();
@@ -71,11 +75,15 @@ public boolean isPathEmpty() {
7175
return path.isEmpty();
7276
}
7377

78+
public ReadOnlyBooleanProperty atDestinationProperty() {
79+
return isAtDestinationProp.getReadOnlyProperty();
80+
}
81+
7482
/**
7583
* @return true when the path is empty and entity is no longer moving
7684
*/
7785
public boolean isAtDestination() {
78-
return !isMoving() && isPathEmpty();
86+
return isAtDestinationProp.get();
7987
}
8088

8189
public AStarGrid getGrid() {
@@ -99,6 +107,8 @@ public Optional<AStarCell> getCurrentCell() {
99107
public void stopMovementAt(int cellX, int cellY) {
100108
path.clear();
101109
moveComponent.setPositionToCell(cellX, cellY);
110+
111+
isAtDestinationProp.set(true);
102112
}
103113

104114
public void stopMovement() {
@@ -153,6 +163,8 @@ public void moveToCell(int x, int y) {
153163
* This can be used to explicitly specify the start X and Y of the entity.
154164
*/
155165
public void moveToCell(int startX, int startY, int targetX, int targetY) {
166+
isAtDestinationProp.set(false);
167+
156168
if (moveComponent.isAtDestination()) {
157169
path = pathfinder.get().findPath(startX, startY, targetX, targetY);
158170
} else {
@@ -162,6 +174,10 @@ public void moveToCell(int startX, int startY, int targetX, int targetY) {
162174

163175
@Override
164176
public void onUpdate(double tpf) {
177+
if (!isAtDestination() && !isMoving() && isPathEmpty()) {
178+
isAtDestinationProp.set(true);
179+
}
180+
165181
if (path.isEmpty() || !moveComponent.isAtDestination())
166182
return;
167183

fxgl-entity/src/test/kotlin/com/almasb/fxgl/pathfinding/astar/AStarMoveComponentTest.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,43 @@ class AStarMoveComponentTest {
151151
}
152152
}
153153

154+
@Test
155+
fun `atDestinationProperty correctly reports changes`() {
156+
var count = 0
157+
158+
aStarMoveComponent.atDestinationProperty().addListener { _, hasNotReached, hasReached ->
159+
if (hasReached) {
160+
count = 1
161+
}
162+
163+
if (hasNotReached) {
164+
count = -1
165+
}
166+
}
167+
168+
assertTrue(aStarMoveComponent.isAtDestination)
169+
assertTrue(aStarMoveComponent.atDestinationProperty().value)
170+
171+
aStarMoveComponent.moveToCell(3, 0)
172+
173+
assertThat(count, `is`(-1))
174+
175+
repeat(50) {
176+
cellMoveComponent.onUpdate(STEP_SIZE)
177+
aStarMoveComponent.onUpdate(STEP_SIZE)
178+
}
179+
180+
assertFalse(aStarMoveComponent.isAtDestination)
181+
assertFalse(aStarMoveComponent.atDestinationProperty().value)
182+
assertThat(count, `is`(-1))
183+
184+
finishMotion()
185+
186+
assertTrue(aStarMoveComponent.isAtDestination)
187+
assertTrue(aStarMoveComponent.atDestinationProperty().value)
188+
assertThat(count, `is`(1))
189+
}
190+
154191
private fun putComponentInMotion(x: Int, y: Int) {
155192
aStarMoveComponent.moveToCell(x, y)
156193
aStarMoveComponent.onUpdate(STEP_SIZE)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* FXGL - JavaFX Game Library. The MIT License (MIT).
3+
* Copyright (c) AlmasB ([email protected]).
4+
* See LICENSE for details.
5+
*/
6+
7+
package intermediate.ai.pathfinding;
8+
9+
import com.almasb.fxgl.app.GameApplication;
10+
import com.almasb.fxgl.app.GameSettings;
11+
import com.almasb.fxgl.entity.Entity;
12+
import com.almasb.fxgl.pathfinding.CellMoveComponent;
13+
import com.almasb.fxgl.pathfinding.CellState;
14+
import com.almasb.fxgl.pathfinding.astar.AStarGrid;
15+
import com.almasb.fxgl.pathfinding.astar.AStarMoveComponent;
16+
import javafx.scene.input.MouseButton;
17+
import javafx.scene.paint.Color;
18+
import javafx.scene.shape.Rectangle;
19+
import javafx.scene.shape.StrokeType;
20+
21+
import static com.almasb.fxgl.dsl.FXGL.debug;
22+
import static com.almasb.fxgl.dsl.FXGL.entityBuilder;
23+
24+
/**
25+
* Demo that uses A* search to find a path between 2 cells in a grid.
26+
* Right click to place a wall, left click to move.
27+
*
28+
* @author Almas Baimagambetov (AlmasB) ([email protected])
29+
*/
30+
public class AStarPathfindingSample extends GameApplication {
31+
32+
private Entity agent;
33+
34+
@Override
35+
protected void initSettings(GameSettings settings) {
36+
settings.setWidth(1280);
37+
settings.setHeight(720);
38+
settings.setDeveloperMenuEnabled(true);
39+
}
40+
41+
@Override
42+
protected void initGame() {
43+
var grid = new AStarGrid(1280 / 40, 720 / 40);
44+
45+
agent = entityBuilder()
46+
.viewWithBBox(new Rectangle(40, 40, Color.BLUE))
47+
.with(new CellMoveComponent(40, 40, 150))
48+
.with(new AStarMoveComponent(grid))
49+
.zIndex(1)
50+
.anchorFromCenter()
51+
.buildAndAttach();
52+
53+
agent.getComponent(CellMoveComponent.class).atDestinationProperty().addListener((o, old, isAtDestination) -> {
54+
if (isAtDestination) {
55+
debug("CellMoveComponent: reached destination");
56+
}
57+
});
58+
59+
agent.getComponent(AStarMoveComponent.class).atDestinationProperty().addListener((o, old, isAtDestination) -> {
60+
if (isAtDestination) {
61+
debug("AStarMoveComponent: reached destination");
62+
}
63+
});
64+
65+
for (int y = 0; y < 720 / 40; y++) {
66+
for (int x = 0; x < 1280 / 40; x++) {
67+
final var finalX = x;
68+
final var finalY = y;
69+
70+
var view = new Rectangle(40, 40, Color.WHITE);
71+
view.setStroke(Color.color(0, 0, 0, 0.25));
72+
view.setStrokeType(StrokeType.INSIDE);
73+
74+
var e = entityBuilder()
75+
.at(x * 40, y * 40)
76+
.view(view)
77+
.buildAndAttach();
78+
79+
e.getViewComponent().addOnClickHandler(event -> {
80+
if (event.getButton() == MouseButton.PRIMARY) {
81+
agent.getComponent(AStarMoveComponent.class).moveToCell(finalX, finalY);
82+
83+
} else if (event.getButton() == MouseButton.SECONDARY) {
84+
grid.get(finalX, finalY).setState(CellState.NOT_WALKABLE);
85+
view.setFill(Color.RED);
86+
}
87+
});
88+
}
89+
}
90+
}
91+
92+
public static void main(String[] args) {
93+
launch(args);
94+
}
95+
}

fxgl-samples/src/main/java/sandbox/ai/pathfinding/PathfindingSample.java

Lines changed: 0 additions & 95 deletions
This file was deleted.

0 commit comments

Comments
 (0)