Up until now, all the animations we have created are based on changing attributes, such as the position, color value, and size, of still images. Therefore, our next enhancement is targeted toward the game objects that inhabit the game world of Canyon Bunny. We want to breathe life into the gold coin and bunny head game objects by creating several animations that are built from sequences of individual images. The resulting effect is very similar in comparison to an ordinary flip book where multiple images are perceived as a continuous animation when shown in rapid succession.
LibGDX provides a class called Animation
that helps us define image sequences and also helps us to pick the right (key) frame from a sequence at a specific time which, for example, depends on the desired frame rate for playback. There is some clever mathematics and state-keeping involved for finding the right frame in an efficient way. Luckily, the Animation
class saves us from having to go down that mathematical road and lets us just do animations the easy way with a variety of extra playback options.
Before we look any further into the Animation
class, let's first cover a great feature of LibGDX's TexturePacker that should be used when working with animations. Whenever TexturePacker builds a new texture atlas, it always scans the end of each image filename for an underscore followed by a number like waterfall_03.png
. In this case, the number 03
is considered as the frame index of this animation, while the animation itself will be named and referred to as waterfall in the texture atlas.
Let's imagine that we have a five-frame animation called waterfall
. So, according to the mentioned pattern for allowing animation frames to be recognized by TexturePacker, we would name our image files as follows:
waterfall_01.png
waterfall_02.png
waterfall_03.png
waterfall_04.png
waterfall_05.png
The following short code example further illustrates the line of action to build animations:
TextureAtlas atlas = assetManager.get("atlas.pack"); AtlasRegion firstFrame = atlas.findRegion("waterfall"); AtlasRegion thirdFrame = atlas.findRegion("waterfall", 3); Array<AtlasRegion> allFrames = atlas.findRegions("waterfall");
Let's assume that the waterfall
animation is now available in the texture atlas (atlas.pack
). To retrieve any given image from a texture atlas, we already know that we can simply call the findRegion()
method and pass in the original filename without its extension to reference it. The preceding code example shows that you can also do this with an animation to get the first frame, or you can pass a second argument to specify a concrete frame index like index number 3
to get the third frame. The next line uses the findRegions()
method instead of findRegion()
. Notice the plural s
in the method's name. This method returns a whole array of frames associated with the supplied name which is stored in the allFrames
variable for now.
The following lines of code show how new animations are built using the Animation
class:
float fps = 1.0f / 15.0f; // Time between frames in seconds Animation aniFirst, aniFirstThird, aniAll, aniAllPingpong; aniFirst = new Animation(fps, firstFrame); aniFirstThird = new Animation(fps, firstFrame, thirdFrame); aniAll = new Animation(fps, allFrames); aniAllPingPong = new Animation(fps, allFrames, Animation.PlayMode.LOOP_PINGPONG);
The constructor of the class takes the time given in seconds that describes the time stepping or delay between the current and the next frame that is to be displayed. In this example, we use a frame rate of 15 frames per second. As the example code shows us further, we can pass in either an arbitrary number of AtlasRegion
objects (our individual frames) or an array of those objects to define what frames should be used and in what particular order. Another feature of the Animation
class is its play mode.
There are six distinct play modes we can choose from. They are as follows:
NORMAL
: This plays the animation once (first frame to last)REVERSED
: This plays the animation once (last frame to first)LOOP
: This plays the animation in a loop (first frame to last)LOOP_REVERSED
: This plays the animation in a loop (last frame to first)LOOP_PINGPONG
: This plays the animation in a loop (first frame, to last, to first)LOOP_RANDOM
: This plays the animation in a loop (random frames)The NORMAL
play mode is used as default if it is not explicitly set in the code.
Finally, the animations are now ready to be used and queried for their frames that should be currently displayed according to calculations. These calculations require one input value, which is the so-called state time, as shown in the following code. The state time is the elapsed time of an animation given in seconds. Usually, game objects keep track of their animation time, as we will see shortly:
TextureRegion region = aniAll.getKeyFrame(stateTime);
Assuming that the stateTime
in the preceding code line yields a value of 0 seconds, the region variable will contain the first frame defined in the aniAll
animation.