Sprite Animation with Android

If you followed the series so far we are pretty knowledgable in handling touches, displaying images and moving them around.

But a moving image it’s a pretty dull sight as it looks really fake and amateurish. To give the characters some life we will need to do more than that. That is what animation is all about. A rock is an inanimate object and even if it is thrown, it doesn’t change its shape. A human on the other hand, is very animated. Try throwing one and you’ll see twitching limbs and even screams in the air.

Let’s just resort to examining walking which is pretty complex in its own. Imagine a human crossing your way (just in 2D). You’ll notice different displays of the body. Left foot in front, right hand in front while the oposite limbs are behind. This slowly changes so the left foot remains behind while the right progresses along with the body. But at one point the cycle repeats. If you don’t close your eyes you see the person progressing smoothly. If you close your eyes and keep them closed for a bit and open them again the person already went on and is in a different position. Try blinking quite rapidly and you will see something like the old black and white comedies. That is low frame rate. More on FPS here.

Actually we do want a low frame rate walking for this tutorial, just like this.



The walking presented above is a bit dodgy but it’s a ripped off version of the sprites from Monkey Island. She’s Elaine Marley.
This is called a Sprite. It is a simple 2 dimensional image or animation.
To be able to re-create the above animation, we need every frame from the walking cycle.

It is a 150 pixels wide image and each frame is 30 pixels wide.
To illustrate it better check the following image.

To get the above animation in android (or iPhone or in any other program for that matter), we need to load up each frame as a separate image and display them at regular intervals one after each other. OR we can load up the big image containing all the frames and use the methods provided by android do slice and dice them on the fly and to display only the relevant frame.
To do that is trivial. We know that we have 5 frames and each frame is 30 pixels wide. We define a rectangle (that will be our selection) which has the width of one frame and the height of the image.
The following image shows how I cut out the first two frames. The rest you should fill in.

Knowing all this let’s go create the project. We will use the knowledge from some previous chapters, most notably about the game loop and the one about the image display (here we set up the thread that triggers the drawing of the graphics item every frame).

We will need an object to animate. We use Elaine from Monkey Island so I will the class ElaineAnimated.java.

public class ElaineAnimated {
	
	private static final String TAG = ElaineAnimated.class.getSimpleName();

	private Bitmap bitmap;		// the animation sequence
	private Rect sourceRect;	// the rectangle to be drawn from the animation bitmap
	private int frameNr;		// number of frames in animation
	private int currentFrame;	// the current frame
	private long frameTicker;	// the time of the last frame update
	private int framePeriod;	// milliseconds between each frame (1000/fps)
	
	private int spriteWidth;	// the width of the sprite to calculate the cut out rectangle
	private int spriteHeight;	// the height of the sprite
	
	private int x;				// the X coordinate of the object (top left of the image)
	private int y;				// the Y coordinate of the object (top left of the image)

}

The private attributes are commented but worth mentioning a few.

  • bitmap is the png file containing all the frames. The second image in this article.
  • sourceRect is the selection rectangle. It is the blue window in the image above. The rectangle moves every frame onto the next.
  • frameTicker this is the java timestamp of the last frame change in the walking sequence. Note that this is not the game FPS but the walking FPS. If we want Elaine to perform a complete walk cycle in a second we set the frame rate for walking to 5 because we have 5 frames. To get a really smooth animation we need 30 frames but it’s not the point now.
  • framePeriod is time in milliseconds that represents the period of time a frame is being displayed. If the cycle completes in 1 second that means for 5 frames the period will be 0.2 seconds. That is, each frame will be displayed for 0.2 seconds.

The constructor is as follows:

public ElaineAnimated(Bitmap bitmap, int x, int y, int width, int height, int fps, int frameCount) {
		this.bitmap = bitmap;
		this.x = x;
		this.y = y;
		currentFrame = 0;
		frameNr = frameCount;
		spriteWidth = bitmap.getWidth() / frameCount;
		spriteHeight = bitmap.getHeight();
		sourceRect = new Rect(0, 0, spriteWidth, spriteHeight);
		framePeriod = 1000 / fps;
		frameTicker = 0l;
	}

I’m assuming that the frames are the same width so I calculate the width of the rectangle by dividing the width of the image with the number of frames. I also pass in the fps which is again, the frames per second of the walk cycle not the game FPS.

Elaine will have an update method of her own as she is an animated object and she needs to look good and she is in charge of dragging her feet. Because the period of the game update cycle and Elaine’s one might be (in this case is) different we pass the actual game time as a variable so we know when we need to display the next frame.
For example the game runs very fast and the update is called every 20 milliseconds and we need to update the frame every 200ms, then the progression of the frame will happen at every 10th game update.

Here’s the code:

	public void update(long gameTime) {
		if (gameTime > frameTicker + framePeriod) {
			frameTicker = gameTime;
			// increment the frame
			currentFrame++;
			if (currentFrame >= frameNr) {
				currentFrame = 0;
			}
		}
		// define the rectangle to cut out sprite
		this.sourceRect.left = currentFrame * spriteWidth;
		this.sourceRect.right = this.sourceRect.left + spriteWidth;
	}

The update is called from the main game panel (check previous entries how that works). This is the update method of the MainGamePanel class.

	public void update() {
		elaine.update(System.currentTimeMillis());
	}

The update method is simple (Elaine’s). It increments the frame if the passed in time (which is the system time when the update method was called) is greater than the last time (frameTicker) the frame was updated plus the period of the next update.
If the next frame is beyond the last, we reset the cycle.

After all that area from which the image will be cut out is defined as the sourceRect.
That’s it. Now let’s go on to display it.

public void draw(Canvas canvas) {
		// where to draw the sprite
		Rect destRect = new Rect(getX(), getY(), getX() + spriteWidth, getY() + spriteHeight);
		canvas.drawBitmap(bitmap, sourceRect, destRect, null);
	}

That is all. We set the destination rectangle as to where to draw the cut out image. It is at Elaine’s position (X and Y set in the constructor).

canvas.drawBitmap(bitmap, sourceRect, destRect, null);

tells android to cut out the image defined by sourceRect from the image contained in bitmap and draw it into the rectangle on the canvas defined by destRect.

The draw is called from the game panel’s render method triggered by the game loop (check previous entries).

The MainGamePanel.java differs slightly from the one from previous chapters. I got rid of all the droidz and added just Elaine.

private ElaineAnimated elaine;

	public MainGamePanel(Context context) {
		//* ... removed ... */

		// create Elaine and load bitmap
		elaine = new ElaineAnimated(
				BitmapFactory.decodeResource(getResources(), R.drawable.walk_elaine) 
				, 10, 50	// initial position
				, 30, 47	// width and height of sprite
				, 5, 5);	// FPS and number of frames in the animation
		
		// create the game loop thread
		thread = new MainThread(getHolder(), this);
		
		//* ... removed ... */
	}

Elaine is instantiated in the panel’s constructor and is given an initial positon of (X=10, Y=50). I pass in the width and the height of the sprite too but that is ignored anyway, but you can modify the code.
The FPS is very important and the number of frames too. FPS says how many frames are to be shown in one second. The last parameter is the number of frames in the cycle.

The thread and activity classes haven’t changed at all. You can find them in the download as they are quite long to be pasted. The image is named walk_elaine.png and it was copied to /res/drawable-mdpi/ so android can pick it up automatically.

If you run the application you should be seeing Elaine performing walking cycles in one place. We should have used jumping as that can be performed in one place but you get the idea.

Elaine Walking

Elaine Walking

Enhancement

To make some neat additions modify Elaine’s draw method so it displays the original image containing the sprites from which the frames are extracted.

	public void draw(Canvas canvas) {
		// where to draw the sprite
		Rect destRect = new Rect(getX(), getY(), getX() + spriteWidth, getY() + spriteHeight);
		canvas.drawBitmap(bitmap, sourceRect, destRect, null);
		canvas.drawBitmap(bitmap, 20, 150, null);
		Paint paint = new Paint();
		paint.setARGB(50, 0, 255, 0);
		canvas.drawRect(20 + (currentFrame * destRect.width()), 150, 20 + (currentFrame * destRect.width()) + destRect.width(), 150 + destRect.height(),  paint);
	}

This just displays the image at (20, 150) and creates a new paint object so we can paint over the current frame on the original image.
The method setARGB creates a semi-transparent green paint. The first value is 50 which means it’s 75% transparent. 0 is completely transparent while 255 is fully opaque.
After everything was drawn we paint a rectangle of the size of a frame onto the original image so you see which frame is being displayed in the motion.

Walking with Current Frame Painted

Walking with Current Frame Painted

That’s it. Run it and you have your first sprite animation.

Download the source code here (animation_walk.tar.gz).
LinkedInRedditTumblrDiggTechnorati FavoritesShare

27 Responses - Add Yours+

  1. Ramon says:

    Thank you very much! Your approach was the only that worked out for me! Appreciate it!

    • Gorgo says:

      Have you tried with a multi-row spritesheet?
      I can’t figure out how to specify a rect in other rows :(

  2. K-Dawg says:

    Great Tutorial Thanks. I have a question tho. How would i get a custom delay between each frame. for example I have a breathing/idle animation for my char, when he inhales i want him to stay in “full lungs” position for a while, then exhale. i have a 5 frame animation and want first three frames to be displayed fairly fast but then stop for a second and display the rest of frames. Any help is welcome. Thanks :)

  3. tombadger says:

    Nice tutorial.

    I redo Elaine file, so he is now moving correctly.
    File can be found here: http://www.multiupload.nl/09LMIJVSH3

    Full Elaine sprite: http://plebeo28.altervista.org/immagini/marley.gif

  4. freely says:

    Nice! Great tutorial!

    Can you tell me how i can get my sprite fullscreen?

  5. vincent says:

    if(gameTime > frameTicker + framePeriod){
    frameTicker = gameTime;
    currentFrame++;
    if(currentFrame >= frameNr){
    currentFrame = 0;
    }

    frameTicker = gameTime;
    }

    should the Elaine update the frameTicker every time update…

  6. Mani says:

    how to sprite animation on OPENGL?

  7. Ganapathy says:

    Hi,
    Good tutorial ….

    i am using this and all u=your game concept ,,,

    but while i am trying to load many sprite the app is getting
    OutOfmemory Exception

    How can i solve this error…?

    Help me ASAP…

    Thanks,
    Ganapathy

  8. Oak says:

    when i press Home or Back and comeback to app again, it will show “force close”. how to fix it?

    Thank you.

  9. Sam says:

    Great tutorial, I just ran into one problem though and was wondering if there is a way to fix it.
    I created my own sprite in gimp that is 18 frames long and changed all the frames ect in the code but when I run it the animation moves across the screen until it gets to the last frame then jerks back into the original position, I was wondering why this happens and how to fix it.
    Any help with this issue would be greatly appreciated
    Thanks in advance
    Sam

  10. Sam says:

    Great tutorial but I ran into an issue, I created my own sprite in gimp that is 18 frames and changed everything so it matches up nicely and ran it, but when I ran it the animation started to move across the screen a bit until it got to the last frame and then jerked back to the original position, this does not make sense to me why this happens and was wondering if anyone has an answer to this issue?
    Any help would be greatly appreciated
    Thanks in advance
    Sam

  11. Murat says:

    How can I reverse it? How can I walk back with same sprites?

  12. Peter says:

    Dude, thank you for this great tutorial – it’s very good helped me.

  13. Ferrell says:

    Great tutorial, Thanks!

  14. Febiyan says:

    Hello, thank you for your great tutorial. However, I have some issues that I stumbled upon. I wonder why does the FPS drops when I switch the emulator to landscape mode? The landscape mode in 800×480 (hdpi) drops the FPS to 11 FPS, while the potrait emulator gives me about 30 FPS. :|

    No, I didn’t resize the emulator to 4 inch. :D

  15. Anirudh says:

    @impaler

    dude…need a bit of help here.
    I’m trying to make a sprite that is stationary at the start but once you click somewhere on the screen, it should move to that point automatically in a straight line, creating the walking effect.
    How do I do this?
    Please reply ASAP.

    Cheers
    \m/

  16. Ilaria says:

    And if I have to start the animation after particular conditions, what I have to do? I mean: I have a bitmap that I move. When the bitmap has particular coordinates I’d like to start the sprite animation… How can I do this?

    • kasur says:

      Ilaria: one of the possible solution is to add additional check in the update method of the animated sprite. The other solution may be to implement some kind of the scheduler.

  17. John says:

    Nice! Hm….what if I wanted the sprite to be clickable?

    • BrouzEx says:

      You know where the sprite is on the screen in moment when you are going to click it (beacuse you have its position while drawing). So just use onTouchEvent method and put IF inside where you will limit your field of touch detection (by position of the bitmap). Hope you understand what I wanted to say :)

      Sorry on my bad english

  18. Vadim says:

    Hi!
    Great tutorial.
    I am wondering if there is a way to draw a mirrored sprite without having to load another mirrored sprite sheet?

  19. Luiz says:

    Great tutorial. I’d like to use the accelerometer to move to Elaine. Any idea?

  20. Alif says:

    Very concise and clear tutorial. Thanks for sharing!
    I have following this from the start and able to develop a simple game engine for android. Will be waiting for the next part of tutorial! (tiling or collision system maybe? :D )

    • Impaler says:

      Was thinking more on exploring OpenGL ES for the next step. I am also working on some simple collision detection engine and some particle systems so stay tuned because the interesting bits are just about to start.

      Tiling is on the list as well.