Repository navigation
Neighbor Strategies
This defines how an entity moves.
A* works by expanding "neighbors". The Neighbor Strategy tells the pathfinder which nodes are considered neighbors. Do you walk like a rook (Cardinal)? Like a queen (Diagonal)? Or do you teleport 5 blocks at a time?
We included the two most common ones so you don't have to write vector math.
-
VERTICAL_AND_HORIZONTAL (The Rook)
-
Movement: 6 Directions (Up, Down, North, South, East, West).
-
Use Case: Old-school RPGs, pipes, or entities that can't cut corners.
-
Math: Manhattan distance.
-
-
DIAGONAL_3D (God Mode)
-
Movement: 26 Directions. Connects to every single block touching the current one (corners and edges included).
-
Use Case: Spectator cameras, flying entities, swimming.
-
Warning: Checks 26 nodes per step instead of 6. Slightly heavier on the CPU, but finds smoother paths.
-
INeighborStrategy is a functional interface. It just wants a list of PathVector offsets. You can define anything here. Knight jumps (Chess)? Teleportation? 2D-only movement?
A tank drives on the ground (X/Z) and can cut corners (Diagonal), but it can't fly (Y). Since the movement rules are the same everywhere, just use a lambda.
// No class needed. Just a lambda.
INeighborStrategy flatDiagonalStrategy = () -> List.of(
// Cardinal
new PathVector(1, 0, 0), new PathVector(-1, 0, 0),
new PathVector(0, 0, 1), new PathVector(0, 0, -1),
// Diagonals
new PathVector(1, 0, 1), new PathVector(-1, 0, -1),
new PathVector(1, 0, -1), new PathVector(-1, 0, 1)
);Sometimes, "who is my neighbor" depends on "where am I". For example: Hexagon Maps with Offset Coordinates. The neighbors change depending on whether your row (Y) is even or odd.
For this, we overloaded the interface. You can override getOffsets(PathPosition currentPos).
⚠️ WARNING: READ THIS OR CRY LATER This method is called thousands of times per second inside the hot loop. If you writenew ArrayList(...)inside this method, the Garbage Collector will personally come to your house and unplug your server.Pre-allocate your lists. Return references. Do not allocate memory in the hot path.
public class HexStrategy implements INeighborStrategy {
// PRE-ALLOCATE EVERYTHING.
// We do not pay for memory allocation inside the loop. We are professionals.
private static final List<PathVector> EVEN_ROW = List.of( /* ... 6 vectors ... */ );
private static final List<PathVector> ODD_ROW = List.of( /* ... 6 other vectors ... */ );
@Override
public Iterable<PathVector> getOffsets() {
return Collections.emptyList(); // Not used by the engine if you override the one below
}
@Override
public Iterable<PathVector> getOffsets(PathPosition currentPos) {
// Fast. Zero allocation. Just a pointer switch.
return (currentPos.getY() % 2 == 0) ? EVEN_ROW : ODD_ROW;
}
}Here is a pro-tip: The vectors don't have to be length 1.0.
-
High Precision (< 1.0): Use vectors like 0.5. The pathfinder will check sub-grid positions.
-
Result: Extremely smooth, high-resolution paths.
-
Cost: Heavy performance hit (more steps to cover the same distance).
-
-
Low Density (> 1.0): Use vectors like 5.0. The pathfinder "jumps" 5 blocks at a time.
-
Result: Lightning-fast long-distance searches.
-
Risk: Might jump over a thin wall or miss a narrow door.
-
Plug it in.
PathfinderConfiguration config = PathfinderConfiguration.builder()
// Built-in
.neighborStrategy(NeighborStrategies.DIAGONAL_3D)
// OR Custom Lambda
.neighborStrategy(flatDiagonalStrategy)
// OR Context-Aware Class
.neighborStrategy(new HexStrategy())
.build();