Build HUD, menus, dialogs, overlays, and scenes in FXGL — add UI nodes to the HUD, create custom main and game menus via SceneFactory, open modal dialogs (message, confirm, input, error, progress), push in-game notifications, implement GameSubScene overlays, bind text to game variables, control viewport and camera-follow, integrate FXML layouts, apply custom CSS, use nine-slice scaling images, add scrolling backgrounds, and add a minimap. Use this skill for anything related to game UI, HUD, screen transitions, or in-game menus.
Build HUD, menus, dialogs, overlays, and scenes in FXGL — add UI nodes to the HUD, create custom main and game menus via SceneFactory, open modal dialogs (message, confirm, input, error, progress), push in-game notifications, implement GameSubScene overlays, bind text to game variables, control viewport and camera-follow, integrate FXML layouts, apply custom CSS, use nine-slice scaling images, add scrolling backgrounds, and add a minimap. Use this skill for anything related to game UI, HUD, screen transitions, or in-game menus.
// Requires NotificationService
settings.addEngineService(NotificationService.class);
getNotificationService().pushNotification("Achievement Unlocked: First Kill!");
// Notification appears at top of screen and auto-dismisses
// Place stylesheet at: assets/ui/my-style.css
settings.setCSSList(List.of("my-style.css"));
// Or apply at runtime to a specific node
node.getStyleClass().add("my-button");
// CSS: .my-button { -fx-background-color: #ff0000; -fx-text-fill: white; }
Nine-Slice Scalable Image
// Scales image corners intact, stretches only center/edgesNineSliceImageViewnineSlice=newNineSliceImageView(
getAssetLoader().loadImage("ui/panel.png"),
20, 20, 20, 20// insets: top, right, bottom, left
);
nineSlice.setPrefSize(400, 300); // resize without distorting corners
addUINode(nineSlice, 50, 50);
Scrolling Background
// Adds to UI layer, scrolls based on viewport movement// Self-scrolling (auto-moves independent of viewport):
addUINode(newSelfScrollingBackgroundView(
getAssetLoader().loadTexture("bg/clouds.png"),
ScrollingBackgroundView.Direction.LEFT, 30.0), 0, 0);
// Viewport-coupled (parallax):ScrollingBackgroundViewbg=newScrollingBackgroundView(
getAssetLoader().loadTexture("bg/mountains.png"),
getAppWidth(), getAppHeight(), 0.3); // 0.3 = parallax factor
addUINode(bg, 0, 0);
HUD nodes use screen coordinates, NOT world coordinates — addUINode(node, 50, 50) places
the node 50px from the top-left of the window, regardless of viewport scroll.
initUI() runs after initGame() — access world state freely here. The reverse is
not safe.
GameSubScene pushed on top suspends the game loop only if isPausedWhenOpen() returns
true (default is true). Override it to return false for non-pausing overlays like inventory.
FXML files must be in assets/ui/ and their controllers must be in a package
on the class path. Use ui.getControllerFor(node) only once per node load.
Viewport bounds must be set AFTER setLevelFromMap() because level dimensions are
only known after the level loads.
Minimap performance degrades with thousands of entities. Filter what's shown:
minimap.setEntityFilter(e -> e.isType(PLAYER, ENEMY)).
SceneFactory must be set in initSettings() — it cannot be changed at runtime.
Custom menu must call fireNewGame() / fireExit() etc. (inherited methods) rather
than getGameController() directly to ensure proper scene transitions.