| name | fxgl-minigames |
| description | Use and extend the FXGL built-in mini-game system — launch lock-picking, sweet-spot, trigger-mash, trigger-sequence (QTE), circuit-breaker, and random-occurrence mini-games via MiniGameService, handle success/failure results, configure difficulty, and author custom mini-games. Use this skill when adding interactive skill-check moments, lock- picking mechanics, quick-time events, button-mashing sequences, or any mini-game overlay.
|
| triggers | ["mini game","minigame","MiniGameService","lock pick","sweet spot","QTE","quick time event","trigger mash","circuit breaker","random occurrence"] |
| compatibility | Java 17+, FXGL 21.x. MiniGameService must be registered.
|
| category | fxgl/minigames |
| tags | ["fxgl","java","javafx","minigames"] |
| metadata | {"author":"fxgl-skills","version":"1.0","fxgl-version":"21.1"} |
| allowed-tools | ["Read","Write","Edit","Bash"] |
FXGL Mini-Game System
Setup
settings.addEngineService(MiniGameService.class);
Launching a Mini-Game
All mini-games share the same launch pattern:
getMiniGameService().startMiniGame(
new SomeMiniGame(),
result -> {
if (result.isSuccess()) {
} else {
}
}
);
The mini-game view overlays the GameScene. The underlying game loop continues unless the
mini-game itself pauses it.
Lock Picking Mini-Game
The player rotates a pick to find the sweet spot for each pin.
LockPickMiniGame lockPick = new LockPickMiniGame();
lockPick.setNumPins(3);
lockPick.setTimeLimit(Duration.seconds(30));
getMiniGameService().startMiniGame(lockPick, result -> {
LockPickResult lpr = (LockPickResult) result;
if (lpr.isSuccess()) {
openChest();
play("sounds/unlock.wav");
} else {
breakLockPick();
inc("lockPicksUsed", +1);
}
});
Sweet Spot Mini-Game
A bar oscillates; the player presses a key when it's inside the target zone.
SweetSpotMiniGame sweetSpot = new SweetSpotMiniGame();
sweetSpot.setBarSpeed(2.0);
sweetSpot.setTargetWidth(0.2);
sweetSpot.setTimeLimit(Duration.seconds(5));
getMiniGameService().startMiniGame(sweetSpot, result -> {
SweetSpotResult ssr = (SweetSpotResult) result;
if (ssr.isSuccess()) {
double accuracy = ssr.getAccuracy();
int bonus = (int)(accuracy * 100);
inc("score", bonus);
}
});
Trigger Mash Mini-Game (Button Spam)
The player must press a key a set number of times before a timer runs out.
TriggerMashMiniGame triggerMash = new TriggerMashMiniGame();
triggerMash.setTrigger(new KeyTrigger(KeyCode.SPACE));
triggerMash.setRequiredPresses(20);
triggerMash.setTimeLimit(Duration.seconds(5));
getMiniGameService().startMiniGame(triggerMash, result -> {
if (result.isSuccess()) wrestleEnemy();
else playerTakesDamage(20);
});
Trigger Sequence Mini-Game (QTE)
The player must press keys in the exact displayed order within a time limit.
List<KeyCode> sequence = List.of(KeyCode.UP, KeyCode.UP, KeyCode.DOWN, KeyCode.LEFT, KeyCode.RIGHT);
TriggerSequenceMiniGame qte = new TriggerSequenceMiniGame();
qte.setSequence(sequence.stream().map(KeyTrigger::new).collect(Collectors.toList()));
qte.setInputTimeout(Duration.millis(800));
getMiniGameService().startMiniGame(qte, result -> {
if (result.isSuccess()) { performSpecialMove(); play("sounds/success.wav"); }
else { play("sounds/fail.wav"); }
});
Circuit Breaker Mini-Game
The player navigates a maze of electrical circuits without touching walls.
CircuitBreakerMiniGame circuit = new CircuitBreakerMiniGame();
circuit.setMazeWidth(10);
circuit.setMazeHeight(8);
circuit.setPathWidth(12.0);
getMiniGameService().startMiniGame(circuit, result -> {
if (result.isSuccess()) hackTerminal();
else activateAlarm();
});
Random Occurrence Mini-Game
A visual cue appears randomly; the player must react within a short window.
RandomOccurrenceMiniGame randOccurrence = new RandomOccurrenceMiniGame();
randOccurrence.setDelayRange(Duration.seconds(0.5), Duration.seconds(3));
randOccurrence.setReactionWindow(Duration.millis(400));
randOccurrence.setTrigger(new KeyTrigger(KeyCode.F));
getMiniGameService().startMiniGame(randOccurrence, result -> {
if (result.isSuccess()) dodgeAttack();
else playerTakesHit();
});
Custom Mini-Game
public class PuzzleMiniGame extends MiniGame<PuzzleResult> {
@Override
protected MiniGameView createView() {
Pane root = new Pane();
return new MiniGameView(root);
}
@Override
public void onOpen() {
}
@Override
public void onClose() {
}
private void onPlayerSolves() {
setResult(new PuzzleResult(true, score));
}
}
public class PuzzleResult extends MiniGameResult {
private final int score;
public PuzzleResult(boolean success, int score) {
super(success);
this.score = score;
}
public int getScore() { return score; }
}
getMiniGameService().startMiniGame(new PuzzleMiniGame(), result -> {
PuzzleResult pr = (PuzzleResult) result;
inc("score", pr.getScore());
});
Collision-Triggered Mini-Game Pattern
onCollisionBegin(EntityType.PLAYER, EntityType.LOCKED_CHEST, (player, chest) -> {
if (getCutsceneService().isActive() || getMiniGameService().isActive()) return;
chest.removeFromWorld();
SweetSpotMiniGame mg = new SweetSpotMiniGame();
mg.setBarSpeed(1.5 + geti("difficulty") * 0.5);
mg.setTargetWidth(0.3 - geti("difficulty") * 0.05);
getMiniGameService().startMiniGame(mg, result -> {
if (result.isSuccess()) {
spawn("chestContents", chest.getX(), chest.getY());
play("sounds/chest_open.wav");
} else {
spawn("lockedChest", chest.getX(), chest.getY());
play("sounds/lock_fail.wav");
}
});
});
Gotchas
getMiniGameService().isActive() — always check before launching a new mini-game.
Starting one while another is active will crash.
setResult(...) ends the mini-game — call it exactly once. Calling it multiple times
(e.g., from a timer and a button press) fires the result callback twice.
- Mini-game UI is added on top of GameScene — it does NOT replace the scene. The physics
world and existing entities remain active unless you pause explicitly.
TriggerSequenceMiniGame displays the sequence as on-screen key icons. If the player
has a custom key scheme, update the sequence triggers to match.
LockPickMiniGame difficulty scales linearly with pin count — 1 pin is trivial, 6 pins
is very hard. Calibrate to player skill level with a difficulty variable.
- Register
MiniGameService before getCutsceneService() if both are used — service
order in initSettings matches initialisation order.