FXGL is a 2D and 3D game engine built on top of JavaFX, written in Java and Kotlin by Dr. Almas Baimagambetov. This module is an Aussom wrapper around that library -- the engine itself is FXGL; Aussom exposes its public API so games can be written in Aussom without writing any Java. The wrappers cover the application lifecycle, the entity-component system, input, physics, UI, audio, animation, dialogue, mini-games, virtual controllers, the asset loader, and most of the supporting builders and services.
In Aussom, the FXGL surface lives in two places:
fxgl -- a static class exposing the FXGL DSL (the fxgl.gameApp
entry point, plus helpers like fxgl.getInput, fxgl.set,
fxgl.onKey, etc.).fxgl.<Class> -- one Aussom class per wrapped FXGL Java class
(fxgl.Entity, fxgl.GameSettings, fxgl.CollisionHandler, and
so on).Include only the wrapper classes you actually use. The fxgl static
class is always available once fxgl itself is included.
| Area | Common Classes |
|---|---|
| Lifecycle and launch | fxgl.gameApp, fxglApp, GameSettings |
| Entities and views | Entity, EntityBuilder, Component, ViewComponent |
| Input | fxgl.onKey*, Input, UserAction |
| Physics | PhysicsComponent, CollisionHandler, PhysicsWorld, BoundingShape |
| World scene | GameWorld, GameScene, Viewport |
| World variables | fxgl.set, fxgl.geti, fxgl.onIntChange, PropertyMap |
| UI and text | fxgl.addText, fxgl.addVarText, UIFactoryService, NotificationService |
| Assets and audio | AssetLoaderService, fxgl.loopBGM, fxgl.play |
| Animation | AnimationBuilder, Animation, AnimationChannel |
fxgl.gameApp(title, width, height) returns an fxglApp you configure
by registering callbacks. Each callback is a method reference (::name).
Call launch() last; it blocks until the game exits.
| Hook | Fires | Typical work |
|---|---|---|
onInitSettings(Cb) |
Once, before JavaFX starts | Configure GameSettings |
onPreInit(Cb) |
Once, before input init | Register services |
onInitInput(Cb) |
Once | Bind keys and mouse buttons |
onInitGameVars(Cb) |
Once | Seed world properties |
onInitGame(Cb) |
Once | Spawn entities, set up world |
onInitPhysics(Cb) |
Once | Register collision handlers, gravity |
onInitUI(Cb) |
Once | Build the heads-up display |
onUpdate(Cb) |
Every frame | Per-tick logic; receives tpf (seconds) |
Source: src/com/lehman/aussom/stdlib/aus/fxgl.aus:1039-1089.
A program that opens a 320 x 240 game window and exits after half a second. This is the same pattern used by the integration tests:
include fxgl;
class MyGame {
public app = null;
public elapsed = 0.0;
public run() {
this.app = fxgl.gameApp("My First Game", 320, 240);
this.app.onUpdate(::tick);
this.app.launch();
}
public tick(tpf) {
this.elapsed = this.elapsed + tpf;
if (this.elapsed > 0.5) { this.app.exit(); }
}
}
new MyGame().run();
launch() blocks the calling thread. tpf is the elapsed time for
the current frame in seconds. See
tests/fxgl/integration/integrationTest.aus:67-71 for a working
reference.
GameSettings is passed to your onInitSettings callback. Common
setters:
include fxgl;
include fxgl.GameSettings;
public configureSettings(s) {
s.setTitle("Coin Hunter")
.setVersion("1.0")
.setWidth(800)
.setHeight(600)
.setMainMenuEnabled(true)
.setGameMenuEnabled(true)
.setIntroEnabled(false);
}
All setters return this so they chain. Source:
src/com/lehman/aussom/stdlib/aus/fxgl/GameSettings.aus:29-100.
An entity is anything that lives in the game world: the player, an enemy, a bullet, a wall, a coin. Each entity has a position and may carry a visual node, a hit box, a type, and any number of components.
The fluent way to build one is fxgl.entityBuilder():
include fxgl;
include fxgl.Entity;
include fxgl.FxObj;
include fx.Rectangle;
include fx.Color;
public spawnPlayer() {
rect = new Rectangle(20.0, 20.0);
rect.setFill(Color.BLUE());
return fxgl.entityBuilder()
.at(100.0, 100.0)
.view(rect)
.bbox("player-box")
.collidable()
.buildAndAttach();
}
Helpful builder methods (all chain):
.at(x, y) or .at(x, y, z) -- position.atPoint2D(point) -- position from a JavaFX Point2D.view(node) -- attach a visual JavaFX Node.viewTexture(name) -- attach an image asset by file name.bbox(hitBox) or .viewWithBBox(node) -- give it a hit box.collidable() -- shortcut that adds a CollidableComponent.with(component) -- attach any custom Component.type(enumAjo) -- set the collision-handler type tag.build() -- return the Entity wrapper without adding to world.buildAndAttach() -- build and add to the running game worldSource: src/com/lehman/aussom/stdlib/aus/fxgl/EntityBuilder.aus.
You can also work with an Entity directly. Entity.setType("player")
accepts a string, which is enough for most collision setups:
include fxgl.Entity;
e = new Entity();
e.setType("player");
e.addBoundingBox(20.0, 20.0);
e.addViewNode(rect);
Source: src/com/lehman/aussom/stdlib/aus/fxgl/Entity.aus:159-227.
The simplest input API is fxgl.onKey* inside onInitInput:
include fxgl;
public bindInput() {
fxgl.onKey("W", "Move Up", ::moveUp);
fxgl.onKeyDown("SPACE", "Shoot", ::shoot);
fxgl.onKeyUp("ESCAPE", "Quit", ::quitGame);
}
public moveUp() { /* runs every frame W is held */ }
public shoot() { /* runs once on the SPACE press */ }
public quitGame() { /* runs once on the ESCAPE release */ }
Key names are JavaFX KeyCode constants ("W", "SPACE",
"ESCAPE", "UP", "LEFT", etc.). The three forms differ in when
the callback fires:
onKey -- every frame the key is heldonKeyDown -- once on the pressonKeyUp -- once on the releaseThere is a matching trio for the mouse: onBtn, onBtnDown,
onBtnUp. Source: src/com/lehman/aussom/stdlib/aus/fxgl.aus:681-696.
For more control (rebinding, modifier keys, capturing input events)
adopt the running Input and create UserAction objects, the
pattern used in
tests/fxgl/deferred/deferredLifecycleTest.aus:
include fxgl;
include fxgl.Input;
include fxgl.UserAction;
public onInitInput() {
input = Input.adopt(fxgl.getInput());
action = new UserAction("Jump");
action.onActionBegin(::onJump);
input.addActionKey(action, "SPACE");
}
public onJump() { /* ... */ }
A Component carries per-entity behavior that runs each frame.
Create one, register the lifecycle hooks, and attach it:
include fxgl;
include fxgl.Entity;
include fxgl.Component;
public buildMover() {
c = new Component();
c.onAdded(::onAdded);
c.onUpdate(::tick);
c.onRemoved(::onRemoved);
return c;
}
public onAdded(entity) { /* fires once on attach */ }
public tick(tpf) { /* fires every frame */ }
public onRemoved(entity) { /* fires once on detach */ }
Attach to an entity with entity.addComponent(c). Source:
src/com/lehman/aussom/stdlib/aus/fxgl/Component.aus.
Use CollisionHandler for pairwise collision callbacks. Set each
entity's type to a matching string, register a handler, and add it
to the physics world inside onInitPhysics:
include fxgl;
include fxgl.CollisionHandler;
public onInitPhysics() {
handler = new CollisionHandler("player", "coin");
handler.onCollisionBegin(::onPlayerCoin);
fxgl.getPhysicsWorld().invoke("addCollisionHandler", handler.obj);
}
public onPlayerCoin(player, coin) {
coin.invoke("removeFromWorld");
fxgl.inc("coins", 1);
}
Callbacks receive the two Entity objects in the order of the type
pair. Other hooks on CollisionHandler: onCollision (every frame
of contact), onCollisionEnd (contact ends), and onHitBoxTrigger.
Source: src/com/lehman/aussom/stdlib/aus/fxgl/CollisionHandler.aus.
The pattern above (from
tests/fxgl/deferred/deferredLifecycleTest.aus) uses the running
PhysicsWorld directly. For symmetric short-form access there is
also fxgl.onCollisionBegin(typeA, typeB, cb) which takes Java enum
AJOs for the types.
FXGL holds a shared bag of typed properties for the game state. Use
them so any callback can read or watch the same data without passing
it around. Seed them in onInitGameVars:
include fxgl;
public onInitGameVars() {
fxgl.set("hp", 100);
fxgl.set("speed", 1.5);
fxgl.set("name", "Hogan");
fxgl.set("coins", 0);
}
public tick(tpf) {
if (fxgl.geti("hp") <= 0) { this.app.exit(); }
}
public watchCoins() {
fxgl.onIntChange("coins", ::onCoinsChanged);
}
public onCoinsChanged(newCount) { /* react to coin pickups */ }
Typed accessors: geti, getd, gets, getb, geto (object).
Change watchers: onIntChange, onDoubleChange, onStringChange,
onBooleanChange, onObjectChange. Source:
tests/fxgl/integration/integrationTest.aus:88-93 and
src/com/lehman/aussom/stdlib/aus/fxgl.aus:620-732.
The simplest way to put text on screen during play is fxgl.addText
for a static label or fxgl.addVarText for a label bound to a world
property:
include fxgl;
public onInitUI() {
fxgl.addText("Coin Hunter v1.0", 10.0, 20.0);
fxgl.addVarText("coins", 10.0, 40.0);
}
addVarText updates automatically whenever the named property
changes -- a free heads-up display for the score, the player's HP,
the timer, etc. Both helpers return the underlying JavaFX Text
node. Source: src/com/lehman/aussom/stdlib/fxgl/AussomFxgl.java
(addText / addVarText methods).
For full control, build a JavaFX node and add it through the
GameScene -- the pattern used by the integration test:
include fxgl;
include fxgl.GameScene;
include fxgl.InGamePanel;
public onInitUI() {
gs = GameScene.adopt(fxgl.getGameScene());
panel = new InGamePanel(120.0, 80.0, "LEFT");
gs.addUINode(panel);
panel.open();
}
Source: tests/fxgl/integration/integrationTest.aus:123-131.
This puts the pieces together: a player rectangle, a coin entity, an input binding, a collision handler, and a HUD label showing the score.
include fxgl;
include fxgl.GameSettings;
include fxgl.Entity;
include fxgl.CollisionHandler;
include fx.Rectangle;
include fx.Color;
class CoinHunter {
public app = null;
public player = null;
public run() {
this.app = fxgl.gameApp("Coin Hunter", 400, 300);
this.app.onInitSettings(::settings);
this.app.onInitGameVars(::vars);
this.app.onInitGame(::game);
this.app.onInitPhysics(::physics);
this.app.onInitInput(::input);
this.app.onInitUI(::ui);
this.app.launch();
}
public settings(s) { s.setMainMenuEnabled(false); }
public vars() { fxgl.set("coins", 0); }
public game() {
playerRect = new Rectangle(20.0, 20.0);
playerRect.setFill(Color.BLUE());
this.player = fxgl.entityBuilder()
.at(50.0, 50.0).view(playerRect).collidable()
.buildAndAttach();
this.player.setType("player");
coinRect = new Rectangle(16.0, 16.0);
coinRect.setFill(Color.GOLD());
coin = fxgl.entityBuilder()
.at(200.0, 200.0).view(coinRect).collidable()
.buildAndAttach();
coin.setType("coin");
}
public physics() {
h = new CollisionHandler("player", "coin");
h.onCollisionBegin(::onPickup);
fxgl.getPhysicsWorld().invoke("addCollisionHandler", h.obj);
}
public input() {
fxgl.onKey("W", "Up", ::moveUp);
fxgl.onKey("S", "Down", ::moveDown);
fxgl.onKey("A", "Left", ::moveLeft);
fxgl.onKey("D", "Right", ::moveRight);
}
public ui() {
fxgl.addText("Coins:", 10.0, 20.0);
fxgl.addVarText("coins", 60.0, 20.0);
}
public onPickup(player, coin) {
coin.invoke("removeFromWorld");
fxgl.inc("coins", 1);
}
public moveUp() { this.player.translateY(-2.0); }
public moveDown() { this.player.translateY(2.0); }
public moveLeft() { this.player.translateX(-2.0); }
public moveRight() { this.player.translateX(2.0); }
}
new CoinHunter().run();
Every method called above (setMainMenuEnabled, entityBuilder,
setType, translateX/Y, removeFromWorld, onKey, addText,
addVarText, inc, getPhysicsWorld) is wrapped in
src/com/lehman/aussom/stdlib/aus/fxgl/ or on the fxgl DSL static
class.
When you outgrow the basics, these wrappers cover the bigger features
of FXGL. Each lives at src/com/lehman/aussom/stdlib/aus/fxgl/.
| Class | Why look at it |
|---|---|
AnimationBuilder |
Fluent translate / scale / rotate / fade animations with easing |
PhysicsComponent |
jbox2d dynamics: setBodyType, setLinearVelocity, setAngularVelocity |
ProjectileComponent |
Auto-velocity for bullets and arrows; pairs with ProjectileWithAccelerationComponent |
ParticleEmitter and ParticleEmitters |
Spawn particles with built-in fire / explode / smoke recipes |
Viewport |
Camera control, screen bounds, follow-entity helpers |
GameWorld |
Spawn / query entities, register entity factories |
GameScene |
Add UI nodes, switch render layers, manage the scene graph |
Input, UserAction |
Long-form input binding, rebinding, mock-press in tests |
AssetLoaderService, FXGLAssetLoaderService |
Load textures, sounds, music, dialogue files, text |
CutsceneService, Cutscene, DialogueGraph, DialogueNode |
Story scenes and branching dialogue trees |
NotificationService |
Toast notifications |
SaveLoadService |
Save / load progress to FXGL's bundle format |
AchievementService |
Track and unlock named achievements |
MiniGameService |
Trigger-mash, trigger-sequence, circuit-breaker mini games |
VirtualController, VirtualDpad, VirtualJoystick, VirtualMenuKey |
On-screen controls for touch and mobile |
For the full surface, see design/fxgl-audit.md in this repo --
it lists every wrapped class, the methods on each, and the few items
that are deliberately not wrapped.