Phaser 2 and Flump Animations
By: Aaron Malley
Tags: Flump, Adobe Animate, JavaScript, Phaserjs, Animation

Exported Flump atlas of the character.

Technical Breakdown

The original runtime

The plugin we wrote is based off the runtime written for the Flump exporter tool. Flump is open source and is licensed under the MIT license. This meant we were free to use what we needed from Flump to get animations working in Phaser, however, it wasn’t a straight port. Since Flump is written in ActionScript 3 for the Starling framework, bringing it over to Phaser 2 meant integrating Flump so it worked fluidly with the engine.

Asset Loading

Our first step was implementing a custom loader that we could use with Phaser’s load manager. We wanted to be able to use the standard Phaser pipeline to load our Flump assets. With our new Flump atlas loader, we can call `game.load.flumpAtlas(…)`.

preload() {

game.load.flumpAtlas(“demoLibrary”, “assets/library.json”, “assets/atlas0.png”);

}

To load the Flump assets, we provide a key that we will use to identify those assets within the cache object. In this case, our key is “demoLibrary”. The loader will store the `library.json` data using the key, and add the key as a prefix to the image file name. In our example, the file “assets/atlas0.png” is stored as “demoLibrary/atlas0.png”. We need to prefix the library key onto our images because the Flump exporter does not allow us to change the naming scheme it uses when exporting the atlas images. Without the prefixed library key, we would run into naming collisions if the assets for more than one library were loaded.

Library Creation

After we have the Flump assets loading into Phaser’s asset cache, we initialize a Flump library to use its resources. A Flump library is a collection of textures and movie symbols that were exported from Animate. Each symbol is unique to the library it belongs to, and this was something we wanted to make sure remained the case within the plugin.

create() {

game.flump.add(“demoLibrary”);

}

When a library is added to the plugin, it uses the key we provide to pull all the resources it needs from Phaser’s asset cache. This includes “library.json” and any atlas images that were prefixed by our custom loader. Once the data has been retrieved, it will parse “library.json” and create the frame data for each atlas image. With the atlas images paired with frame data, the Flump atlas can be used as if it were any other image atlas.

game.image.add(0, 0, “demoLibrary/atlas0.png”, “orangeSquare”);

Porting the Animation Player

Having our Flump assets integrated into the standard Phaser workflow allowed us to easily begin porting over the animation player from the Flump exporter.

We started by creating a generic Symbol object. A symbol is any object that Flump uses to play an animation. This includes empty placeholder objects, textures, and movies. In practice, our Symbol is just an extended Phaser Image. This allows us to assign any symbol texture data from a Flump atlas. It also allows pooling of texture symbols when they are not being used by a movie.

When Flump exports an animation with rotations, it converts the rotation values into a scale and skew. This means that the plugin needed support for skewing images. Our Symbol object allowed us to add skewing support into Phaser.

Symbol pooling is one of the optimizations we added. We wanted the ability to reuse a movie symbol to play different animations, which meant that when changing animations some symbols would no longer be needed. Instead of allocating and deallocating symbols, we pool and reuse them by assigning new texture data to it. Any symbol not being used remains in the pool until one is needed.

Frame Labels

One of the more interesting features of Flump is the ability to export frame labels from the timeline in Animate. A frame label is just a string given to a keyframe. This is useful because it allows developers to listen for frame labels to trigger in-game events. For example, if our animators create a cutscene animation, they can use frame labels to tell the game when it should begin playing voice over audio or sound effects. Frame labels can also be used in code to tell an animation to play until it reaches some label, or to go to and stop at a specific label. This was an important feature to make sure we carried over into the plugin.

movie.labelEvents.add((label) => {

if (label === “playSfx”) {

sound.play();

}

});

With our Symbol object and symbol pooling in place, we were able to port the Flump runtime over into Phaser. Overall, it went smoothly. We came across a couple bugs that we fixed, and we did not run into any major issues. After everything was done, we had a Flump animation plugin for Phaser 2 that accurately played content created in Adobe Animate.

Speed Control

After the Flump implementation was complete, we found that we wanted to have a greater level of control over playback. While animations did play and animate at the correct frame rate, we wanted the ability to control the playback speed of animations. This would allow us to speed up and slow animations down based on game logic. Adding the feature was not difficult, because it only required scaling the delta time used to advance an animation.

This did introduce an issue when animations were slowed down. They looked clunky; as if the game was running at a lower frame rate even though it was still running at 60 fps. When Flump advances an animation, it rounds its frame position. It does this so it does not run into errors when pulling keyframes from an array. The issue this causes is that when it came to calculating the interpolation between one frame to the next, it did so in whole steps. So frame one to frame two, two to three, and so on. This is good for figuring out which keyframe data to use, however, it does not work for scaling the speed of an animation. To fix this, we allowed the interpolation to use fractions of frames without allowing the animation to play further than the keyframe it is currently on. So if our last frame was one and our next frame was two, then if game time determined we are actually at frame position 1.5, then the animation would use 1.5 to interpolate between the two frames. We still used whole numbers to determine keyframe positions, however now animations can use fraction of frames to interpolate. The result is far smoother animations. They still animate at the frame rate that was set in Adobe Animate, however they play smoother in our plugin. These smoother animations allowed us to scale the playback speed without slower speeds looking clunky.

Conclusion

A GIF of the Flump mascot playing in Phaser 2

After we were able to smooth out animations, the plugin was complete. We were able to create a Flump player for Phaser 2 that allows developers to reuse movies for multiple animations as well as implemented optimizations and improvements.

You can find the source for the Phaser 2 Flump plugin on GitHub here. The plugin is also available on NPM here. We also provide a demo project that shows examples of how to use the plugin here.

What’s Next?

We are currently bringing this plugin over to Phaser 3. Although the Phaser 2 port of Flump was straight forward, Phaser 3 does not support the ability to skew an image. Since Phaser 2 is built on top of PIXI, we were able to easily use Flump’s scale and skew value to calculate a matrix to figure out the rotation of an object. In Phaser 3, the matrix calculations have been decoupled from the renderers and game objects making Flump integration more difficult. When we finally do port Flump to Phaser 3, we want to make sure it is seamlessly integrated into the engine to ensure maximum stability.