| name | fxgl-animation |
| description | Animate entities and JavaFX nodes in FXGL — translate, rotate, scale, and fade entities using AnimationBuilder DSL, play sprite sheet frame animations with AnimatedTexture and AnimationChannel, animate along bezier/path curves, animate JavaFX properties (color, opacity), chain sequential animations, apply easing interpolators, and spawn or despawn entities with built-in scale effects. Use this skill when adding movement tweens, character walk cycles, UI transitions, cutscene animations, or particle-like effects.
|
| triggers | ["AnimationBuilder","animate","tween","sprite sheet","AnimatedTexture","AnimationChannel","fade in","fade out","interpolator","walk cycle","animation"] |
| compatibility | Java 17+, FXGL 21.x
|
| category | fxgl/animation |
| tags | ["fxgl","java","javafx","animation"] |
| metadata | {"author":"fxgl-skills","version":"1.0","fxgl-version":"21.1"} |
| allowed-tools | ["Read","Write","Edit","Bash"] |
FXGL Animation System
AnimationBuilder — Core DSL
All property animations use the builder. Chain options before calling a terminal method.
animationBuilder()
.duration(Duration.seconds(1))
.interpolator(Interpolators.SMOOTH.EASE_IN_OUT())
.translate(entity)
.from(new Point2D(0, 0))
.to(new Point2D(400, 300))
.buildAndPlay();
Animation anim = animationBuilder()
.duration(Duration.seconds(2))
.translate(entity)
.from(new Point2D(0, 0))
.to(new Point2D(600, 400))
.build();
anim.setOnFinished(() -> entity.removeFromWorld());
anim.start();
Translate, Rotate, Scale, Fade
Duration d = Duration.seconds(0.5);
animationBuilder().duration(d).translate(entity).from(start).to(end).buildAndPlay();
animationBuilder().duration(d).rotate(entity).from(0).to(360).buildAndPlay();
animationBuilder().duration(d).scale(entity)
.from(new Point2D(1, 1)).to(new Point2D(2, 2)).buildAndPlay();
animationBuilder().duration(d).scale(entity)
.from(new Point2D(0, 0)).to(new Point2D(1, 1)).buildAndPlay();
animationBuilder().duration(d).fadeOut(entity)
.buildAndPlay()
.setOnFinished(() -> entity.removeFromWorld());
animationBuilder().duration(d).fadeIn(entity).buildAndPlay();
animationBuilder().duration(d).fadeTo(entity, 0.5).buildAndPlay();
Spawn / Despawn with Scale Effect (DSL Convenience)
Entity e = spawnWithScale("enemy", new SpawnData(200, 300), Duration.seconds(0.3),
Interpolators.ELASTIC.EASE_OUT());
despawnWithScale(entity, Duration.seconds(0.3), Interpolators.ELASTIC.EASE_IN());
Entity e2 = spawnFadeIn("coin", new SpawnData(400, 200), Duration.seconds(0.5));
despawnWithDelay(entity, Duration.seconds(2));
Chaining Sequential Animations
animationBuilder().duration(Duration.seconds(0.5))
.translate(entity).from(A).to(B)
.build()
.setOnFinished(() ->
animationBuilder().duration(Duration.seconds(0.5))
.rotate(entity).from(0).to(180)
.build()
.setOnFinished(() ->
animationBuilder().duration(Duration.seconds(0.3))
.fadeOut(entity).buildAndPlay())
.start())
.start();
animationBuilder()
.duration(Duration.seconds(1))
.autoReverse(true)
.repeat(3)
.scale(entity)
.from(new Point2D(1, 1))
.to(new Point2D(1.3, 1.3))
.buildAndPlay();
animationBuilder()
.duration(Duration.seconds(0.5))
.repeatInfinitely()
.autoReverse(true)
.translate(entity)
.from(new Point2D(entity.getX(), entity.getY() - 10))
.to(new Point2D(entity.getX(), entity.getY() + 10))
.buildAndPlay();
Path Animation (Bezier Curve)
CubicCurve path = new CubicCurve(
100, 500,
200, 100,
600, 100,
700, 500
);
animationBuilder()
.duration(Duration.seconds(3))
.alongPath(entity, path)
.buildAndPlay();
Animate JavaFX Property (Custom Value)
AnimatedValue<Double> av = new AnimatedValue<>(0.0, 1.0);
animationBuilder()
.duration(Duration.seconds(2))
.animate(av)
.onProgress(value -> {
myShaderNode.setOpacity(value);
colorRect.setFill(Color.color(value, 0, 1 - value));
})
.buildAndPlay();
Animated String (Text Reveal)
Text label = getUIFactoryService().newText("", Color.WHITE, 20);
addUINode(label, 200, 100);
animationBuilder()
.duration(Duration.seconds(2))
.animateString(label, "Hello, World!")
.buildAndPlay();
Interpolator Reference
Interpolators.SMOOTH.EASE_IN()
Interpolators.SMOOTH.EASE_OUT()
Interpolators.SMOOTH.EASE_IN_OUT()
Interpolators.ELASTIC.EASE_OUT()
Interpolators.ELASTIC.EASE_IN()
Interpolators.BOUNCE.EASE_OUT()
Interpolators.BACK.EASE_OUT()
Interpolators.EXPONENTIAL.EASE_IN()
Interpolators.EXPONENTIAL.EASE_OUT()
Interpolators.LINEAR
Interpolator.EASE_BOTH
Sprite Sheet Animation
Texture spriteSheet = getAssetLoader().loadTexture("characters/player.png");
AnimationChannel idleChannel = new AnimationChannel(spriteSheet, 4,
64, 64, Duration.seconds(0.8), 0, 3);
AnimationChannel walkChannel = new AnimationChannel(spriteSheet, 8,
64, 64, Duration.seconds(0.6), 4, 11);
AnimationChannel jumpChannel = new AnimationChannel(spriteSheet, 8,
64, 64, Duration.seconds(0.4), 12, 14, false);
AnimatedTexture animTex = new AnimatedTexture(idleChannel);
animTex.loop();
entityBuilder()
.view(animTex)
.buildAndAttach();
public void startWalking() {
animTex.loopAnimationChannel(walkChannel);
}
public void stopWalking() {
animTex.loopAnimationChannel(idleChannel);
}
public void jump() {
animTex.playAnimationChannel(jumpChannel);
animTex.setOnCycleFinished(() -> animTex.loopAnimationChannel(idleChannel));
}
AnimationChannel Constructor Variants
new AnimationChannel(texture, frameCount, frameW, frameH, duration, startFrame, endFrame)
new AnimationChannel(texture, frameCount, frameW, frameH, duration, startFrame, endFrame, loop)
new AnimationChannel(List.of(frame0, frame2, frame5), duration)
new AnimationChannel(image, frames, frameW, frameH, duration, start, end)
Delay Before Animation
animationBuilder()
.delay(Duration.seconds(0.5))
.duration(Duration.seconds(1))
.fadeIn(entity)
.buildAndPlay();
Gotchas
buildAndPlay() vs build().start(): buildAndPlay() is fire-and-forget with no
handle. Use build() when you need to stop, pause, or set an onFinished callback.
- Animations do not block the game loop — all animations run asynchronously on the
JavaFX animation timer. Use
setOnFinished() to sequence code after an animation.
translateX/Y in from/to are absolute world coordinates, not offsets. To offset
from current position: .from(entity.getPosition()).to(entity.getPosition().add(100, 0)).
- AnimatedTexture must be the entity's view — you cannot reuse the same
AnimatedTexture
instance across multiple entities. Create a new one per entity.
loopAnimationChannel vs playAnimationChannel: loop plays forever; play fires
onCycleFinished once the animation ends once.
- Sprite sheet orientation: FXGL reads frames left-to-right, top-to-bottom. Frame index 0
is top-left. Frame (col, row) = index row * numCols + col.
animationBuilder(scene) variant: pass a specific Scene when animating UI nodes
that live in a GameSubScene rather than the main GameScene.