This very simple tutorial will quickly bring your games to life by drawing the frames of animation from a row of frames drawn consecutively in the same file. We will see how to draw each of five frames one after the other and after we have drawn the last frame, loop back to the first. I have just updated this tutorial and added an extra code listing to the end which gives an example of how you might encapsulate the animation functionality in a class.

patreon

 

 

About this project

Skill level 1
Time to complete 1 hour

New Concepts:

  1. Internal animations from a sprite sheet
  2. Using official documentation
  3. Overloaded methods

Recommended preparation tutorials

Admittedly Bob isn’t the highest quality graphic and his sprite sheet represents an extremely basic walking motion but once you have this technique in your game programming tool-kit you can swap Bob out for some really high-quality sprite sheets and create some graphical masterpieces. There are many free examples to be found with a quick Web search.

Setting up the sprite sheet animation project

To get started create a new project in Android Studio, call it Sprite Sheet Animation, and name the Activity SpriteSheetAnimation. Now download the sprite sheet below and add it to the drawable folder of your Android Studio project. Be sure to keep the file name the same. bob.png. Please don’t be confused by the fact that Bob is facing the opposite way in the sprite sheet to that of the explanatory diagrams later in the project. I did the explanatory diagrams first then realized that Bob would be moonwalking backward if I didn’t amend the Building a simple game engine code. And I wanted to keep the code consistent between the two projects so that just the new sprite sheet animation code stands out.

bob

 

Below you can see a representation of bob.png. We can see the height and width of each frame as well as the progression through a simple walking animation. Note that the actual bob.png file background is transparent not white. It just looks white above because of the white background of this page. With this simple sprite sheet animation solution, the width and height of each frame perhaps surprisingly, not relevant. What is key however is the fact that all the widths are the same and that the ratio of the width to the height is important for the sake of maintaining the visual consistency of the displayed image. So take a quick look before moving on.
5_frame_sprite_sheet_explained

Notice in the next, quite large code block that it is nearly identical to the code from the Building a simple game engine project. Review the code below and refer to the comments for a reminder but if anything is unclear refer to that previous project to understand what is going on. Make note of where we will be adding new code to animate Bob to make him look like he is walking. I have added some comments to mark these places.
[widgets_on_pages id=”udemy_advert_java_2″]
We will add a bunch of new member variables just after the existing member variables shown below. We will add a few lines of code in the constructor SpriteSheetAnimation() just after we create the bitmap from the bob.png file. We will add a new method called getCurrentFrame that will work the magic of our sprite sheet animation and finally, we will change the way that we draw the bitmap to the screen in the draw method. Notice in the code below the original call to drawBitmap is commented out and ready for our sprite sheet-friendly code.

The starting code from the Simple game engine project

So, delete the entire contents of your SpriteSheetAnimation.java code file with the exception of the package declaration, review, and add the code below in its place.

Now, in the code that follows, we will add all the required extra member variables.

Adding the sprite sheet control variables

Firstly we have two int variables frameWidth and frameHeight for holding the width and height of the frame. You might notice that we initialize these two variables to two values which are different to the actual sizes of the frames in the bob.png file as shown in the previous image. As we will see this doesn’t matter and you can make Bob’s frames almost any size you like.

Next, we declare another int called frameCount and initialize it to 5 which is the number of frames in the sprite sheet. Then we declare and initialize currentFrame to 0. This variable will perhaps unsurprisingly keep track of which frame is being drawn and as we want to start with frame zero we initialize it accordingly.

The variable lastFrameChangeTime will hold a value in milliseconds representing the time that the last frame was changed. The int variable frameLengthInMilliseconds is how long we would like each frame of animation to last. 100 milliseconds is one-tenth of a second.

After this, we declare and initialize an object of type Rect which will hold the coordinates (the four corners) of the current frame within our sprite sheet, so we name it frameToDraw and initialize it with 0 (left), 0(top), frameWidth(right) and frameHeight(bottom) which after we have loaded and scaled bob.png will be the exact first frame of animation.

Finally, in this block of code, we declare and initialize a RectF which is exactly the same as a Rect except it can hold floating-point numbers as well. This is ideal because it will hold Bob’s x location which is indeed a float. We name the RectF whereToDraw and initialize it with bobXPosition(left), 0 (top), bobXPosition + frameWidth(right), and frameHeight(bottom). Note the difference between the whereToDraw and frameToDraw objects. The whereToDraw object will be the screen coordinates where we will draw the frame defined by frameToDraw.

Add the code below that we have just discussed at the end of all the previous member variables but before the constructor method just after the // New variables for the sprite sheet animation comment.

Now we can add some code in the constructor.

Scaling the sprite sheet to match the frame size

When Android loads a bitmap from a file it scales it differently depending upon the device the program is running on. So it is very unlikely that the bitmapBob = BitmapFactory.decodeResource... line of code which loads bob.png will correspond with our various frameWidth and frameHeight variables. It is possible to put the bob.png file within a specially named folder within the drawable folder which will then instruct Android not to mess with the scale. However, as our game coding progresses we want to respond to things like screen resolution so we will begin to see part of how we will do so here. In the next block of code which we must add right after the previously mentioned  bitmapBob = BitmapFactory.decodeResource... line of code, we take our new Bitmap object, bitmapBob and scale it using Bitmap.createScaledBitmap and scale it to the size of  frameWidth * frameCount, frameHeight. Now we have a sprite sheet that is exactly the correct size for all the variables we declared previously.

Add the code below, just below the  bitmapBob = BitmapFactory.decodeResource... line of code.

Now we need a new method that will keep track of our frames and loop through them at the rate contained in frameLengthInMilliSeconds (100 milliseconds per frame).

Getting the current frame

The solution is the getCurrentFrame method that we will now discuss. After the method signature, we initialize time to whatever the time is. Then we check whether the player is currently moving Bob because if he isn’t we know that no matter how much time has elapsed we don’t want to change the frame. If isMoving is true we check if time is greater than the time we last changed the frame of animation added to the length of time we want each frame to be visible. If it is then we set lastFrameChangeTime to the current time ( time) ready for the next time we call this method. Then we simply increment the current frame with currentFrame ++.

Next, we check to see if currentFrame has exceeded the frameCount and if it has it is time to go back to the first frame and currentFrame is set to zero. Now outside of all the if statements we want to set frameToDraw to hold the four corners of whatever the current frame of animation is.

This is achieved by setting frameToDraw.left to currentFrame multiplied by frameWidth and frameToDraw.right to whatever frameToDraw.left now is + frameWidth. The Rect frameToDraw now holds the coordinates of the current frame. Enter the new method getCurrentFrame. It can go anywhere within the SpriteSheetAnimation class as long as it is not inside another method.

Finally, we will get to draw Bob.

Drawing the frames of animation

In the next block of code, you can see the old call to canvas.drawBitmap has been commented out. Immediately after this, we call whereToDraw.set and initialize it with the coordinates on the screen where we want to draw a frame of Bob. We use bobXPosition (for left), (for top), bobXPosition + frameWidth (for right) and frameHeight (for the bottom).

Then we call getCurrentFrame which loads the coordinates of the current frame of animation into frameToDraw. The last line of code calls an overloaded version of drawBitmap to draw the appropriate frame from the sprite sheet to the appropriate coordinates on the screen. Take a look at the drawing below to understand this last line of code.

how_drawBitmap_uses_sprite_sheet

 

Overloaded methods are methods that can take multiple different parameters but still have the same name. Now would be a good time to start exploring the official Android documents for the first time if you haven’t ever done so before. Here you can see all the different versions of drawBitmap and the arguments you can use to get stuff drawn to the screen with it. Getting used to the dry, technical but extremely useful official documentation is invaluable. Here is a link to the ins and outs of the Canvas class which includes explanations for several overloaded versions of drawBitmap.

Add these final lines of code in the draw method at the position indicated by the commented-out code.

Final thoughts

You can now run the project and watch Bob stroll confidently and fairly smoothly across the screen. We will use this technique for sprite sheet animation in an upcoming game project where we will see how to wrap this functionality up in a class of its own so it can be used for multiple different animated game objects while only writing the code once. Sprite sheet animation is also discussed at length in my book Android Game Programming by Example.

complete coded listing from SpriteSheetAnimation.java

patreon

 

 

Encapsulating the animation in an Animation class

A few people have asked how you might wrap the animation up into a class. Here is some code that is one way of doing it, there are others. I haven’t got time at the moment to fully explain or add code changes for the code that uses it but you will see that most of the variables and method names remain the same or similar to the tutorial and code above. Obviously, you will need to make changes to the code that uses the class yourself. I hope this helps a bit.