While most of my posts so far dealt with JavaFX properties and bindings, today I want to write about another part of the JavaFX runtime I also work on: the animation API. In this article I will explain how to write custom animations in JavaFX and use this approach to create a class for sprite animations. (This will also be a good practice for one of my sessions at the conference 33rd Degree. I plan to write a game in JavaFX in just one hour. That’s going to be fun!) 🙂
There are many very good articles out there describing predefined Transitions (TranslateTransition, RotateTransition etc.) and Timelines. Most of the time these approaches are sufficient, but in some cases one just needs more flexibility. And that is when the Transition class comes into play, which can be extended to define a custom animation.
To write your own animation class by extending Transition, two steps are required:
- Specify the duration of a single cycle
- Implement the interpolate() method
Duration of a single cycle
You can set the duration of a cycle by calling the protected method setCycleDuration(). Most of the time the duration is either fixed (if the animation is used only once) or configurable by the user. Almost all predefined Transitions in the JavaFX runtime fall into the second category. They expose their cycle duration via the duration property, you probably want to do that in your class, too. In rare cases the duration of a cycle depends on other values. For example the duration of a SequentialTransition and ParallelTransition depend on the duration of their children.
You can change the cycle duration as often as you like, but note that it does not effect a currently running animation. Only after an animation is stopped and started again is the new cycle duration taken into account.
The interpolate() method
The interpolate() method is abstract and needs to be overridden. It defines actual behavior of the animation. The method interpolate() is called by the runtime in every frame while the animation is playing. A value frac, a double between 0.0 and 1.0 (both inclusive) is passed in, which specifies the current position. The value 0.0 marks the start of the animation, the value 1.0 the end. Any value in between defines the relative position. Note that a possible Interpolator is already taken into account when the value of frac is calculated.
The class SpriteAnimation
To demonstrate how a custom transition is defined, we will look at a class that allows us to do Sprite animations. It takes an image that has several frames and moves the viewport from one frame to the other over time. We will test this class with the famous “The Horse in Motion” by Eadweard Muybridge. Enough talk, here is the code:
package sandboxfx; import javafx.animation.Interpolator; import javafx.animation.Transition; import javafx.geometry.Rectangle2D; import javafx.scene.image.ImageView; import javafx.util.Duration; public class SpriteAnimation extends Transition { private final ImageView imageView; private final int count; private final int columns; private final int offsetX; private final int offsetY; private final int width; private final int height; private int lastIndex; public SpriteAnimation( ImageView imageView, Duration duration, int count, int columns, int offsetX, int offsetY, int width, int height) { this.imageView = imageView; this.count = count; this.columns = columns; this.offsetX = offsetX; this.offsetY = offsetY; this.width = width; this.height = height; setCycleDuration(duration); setInterpolator(Interpolator.LINEAR); } protected void interpolate(double k) { final int index = Math.min((int) Math.floor(k * count), count - 1); if (index != lastIndex) { final int x = (index % columns) * width + offsetX; final int y = (index / columns) * height + offsetY; imageView.setViewport(new Rectangle2D(x, y, width, height)); lastIndex = index; } } }
Listing 1: The class SpriteAnimation
For the sake of simplicity, this example class just takes all parameters in the constructor and does not allow to change them later. In most cases this is sufficient.
The class expects an ImageView, the duration of a single cycle (that is how long it should take to go through all frames), the number of frames, the number of columns (how many frames are in one row in the image), the offset of the first frame, and the width and height of all frames. The duration of the full cycle is passed to the super class by calling setCycleDuration(), all other values are stored. As a last step in the constructor, the interpolator is set to linear. By default an easing interpolator is set for all transitions, because that is what usually gives the best results. But in our case we want to run through all frames with the same speed and an easing interpolator would look weird.
The interpolate() method takes the passed in value and calculates which frame needs to be displayed at the moment. If it changed since the last time interpolate() was called, the position of the new frame is calculated and the viewport of the ImageView set accordingly. That’s it.
The Horse in Motion
To demonstrate the SpriteAnimation class, we will animate The Horse in Motion. The code to do that is straightforward, most of the work is already done. It creates an ImageView with the viewport set to the first frame and instantiates the SpriteAnimation class. The parameters are just estimates, you may want to tweak them a little.
package sandboxfx; import javafx.animation.Animation; import javafx.application.Application; import javafx.geometry.Rectangle2D; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.stage.Stage; import javafx.util.Duration; public class SandboxFX extends Application { private static final Image IMAGE = new Image("http://upload.wikimedia.org/wikipedia/commons/7/73/The_Horse_in_Motion.jpg"); private static final int COLUMNS = 4; private static final int COUNT = 10; private static final int OFFSET_X = 18; private static final int OFFSET_Y = 25; private static final int WIDTH = 374; private static final int HEIGHT = 243; public static void main(String[] args) { launch(args); } public void start(Stage primaryStage) { primaryStage.setTitle("The Horse in Motion"); final ImageView imageView = new ImageView(IMAGE); imageView.setViewport(new Rectangle2D(OFFSET_X, OFFSET_Y, WIDTH, HEIGHT)); final Animation animation = new SpriteAnimation( imageView, Duration.millis(1000), COUNT, COLUMNS, OFFSET_X, OFFSET_Y, WIDTH, HEIGHT ); animation.setCycleCount(Animation.INDEFINITE); animation.play(); primaryStage.setScene(new Scene(new Group(imageView))); primaryStage.show(); } }
Listing 2: The Horse in Motion with JavaFX
Conclusion
Defining your own animation by extending the Transition class is simple and straightforward. It is an extremely powerful approach though, because an animation created this way has all the functionality regular animations have. For example you can play it slower and faster by changing the rate and you can play it even backwards. You can run it in a loop and you can use it within ParallelTransition and SequentialTransition to create even more complex animations.
9 responses to “Creating a Sprite Animation with JavaFX”
Hi Michael.
Thanks for this post, it has interesting info.
I’ve blogged about this topic back on javafx 1.3: http://pixelduke.wordpress.com/2010/04/24/modellus-new-animated-characters/
Though at that time I used a different set of framework classes than presented on this post and single image files for each animation frame.
Don’t know which is best: single image files for each frame versus big image file containing all frames, when considering desktop apps.
Hi Pedro,
I do not think it makes much of a difference from a performance perspective. IMO using a single image for each frame is better, if you reuse frames in different animations. On the other hand a single big image is easier to handle, because you have only one file to take care of.
– Michael
Amazing, it’s so smooth without any lag, I just want to test it with more frames !!
Is Shape Tweening planned for JavaFX 2 ? (like JavaFX morph animation)
Hi Sebastian,
I am not aware of a plan to add shape tweening. Why don’t you just add a feature request in JIRA to make sure it gets considered? 🙂
– Michael
Inspiring!!!
Thanks a lot!
[…] Heinrichs has blogged about how to create a sprite animation with JavaFX. The way in which he does it is quite interesting, and well worth reading to understand this area […]
I’ve read some blog posts comparing javafx with flash for animations and interactivity saying javafx is much slower and takes much more time to load. Is this also the case with javafx2? I am planning to create an interactive application for children and speed is really very important for interaction. But I also need to be able to involve speech and character recognition in the application (desktop application for now.) Would you recommend using javafx 2 for this application?
Hi Laila,
sorry for the late reply. I have been very busy lately writing a new application. (Here is a little teaser.)
JavaFX 2 is amazingly fast and I am sure you will not run into any performance issues, if the rest of the application is done right. Just remember to put long running tasks in background threads – that’s usually enough to get good performance on desktop.
Good luck and lots of fun with your project. Sounds very interesting.
– Michael
Creating a Sprite Animation with JavaFX…
Thank you for submitting this cool story – Trackback from JavaPins…