| name | fxgl-tower-defense |
| description | Build a tower defense game in FXGL — implement waypoint-following enemies with WaypointMoveComponent, create a boolean grid for tower placement that excludes path cells, implement tower targeting logic (nearest/strongest/first enemy in range), fire projectiles from towers at enemies, build a wave-based enemy spawning system with configurable intervals, add gold rewards on enemy kills, deduct player lives when enemies reach the exit, implement tower sell/upgrade mechanics, preview tower range with a circle overlay, and support multiple tower types (basic, slow, splash, chain). Use this skill when building a tower defense game, lane defense, or any game where the player places defensive structures against waves of path-following enemies.
|
| triggers | ["tower defense","waypoint enemy","tower placement","wave system","tower range","enemy path","tower targeting","splash tower","slow tower","buy tower","sell tower"] |
| compatibility | Java 17+, FXGL 21.x
|
| category | fxgl/game-types |
| tags | ["fxgl","java","javafx","game-types","tower","defense"] |
| metadata | {"author":"fxgl-skills","version":"1.0","fxgl-version":"21.1"} |
| allowed-tools | ["Read","Write","Edit","Bash"] |
FXGL Tower Defense
Grid Setup
private static final int TILE = 48;
private static final int COLS = 20;
private static final int ROWS = 12;
private boolean[][] occupied = new boolean[COLS][ROWS];
private List<Point2D> waypointPixels;
@Override
protected void initGame() {
setLevelFromMap("td_level.tmx");
waypointPixels = getGameWorld()
.getEntitiesByType(EntityType.WAYPOINT)
.stream()
.sorted(Comparator.comparingInt(e -> e.getInt("order")))
.map(Entity::getPosition)
.toList();
getGameWorld().getEntitiesByType(EntityType.PATH).forEach(e -> {
occupied[(int)(e.getX() / TILE)][(int)(e.getY() / TILE)] = true;
});
}
Enemy with WaypointMoveComponent
@Spawns("enemy")
public Entity newEnemy {
();
waypointMove.setSpeed(data.getOrDefault(, ));
waypointMove.setLooping();
waypointPixels.forEach(waypointMove::addWaypoint);
waypointMove.setOnLastWaypointReached(() -> {
entity.removeFromWorld();
inc(, -);
(geti() <= ) showGameOver();
});
entityBuilder(data)
.type(EntityType.ENEMY)
.view()
.bbox(BoundingShape.circle())
.with(waypointMove)
.with( (data.getOrDefault(, )))
.with( ())
.build();
}