The ABCs of Android game development: Animate sprites

In the third installment of his five-part app developer series, William J. Francis walks through how to use both kinds of sprite animation when developing this Android game.

If you've been following along with this series on Android game development, you know we now have a game set and our sprites in place. The next step is to animate our sprites around the game board. (The goal of our game is to adjust the saucer's momentum in order to avoid getting smashed to bits by the asteroid. While simplistic, the project incorporates all the essential aspects of a video game: a canvas, sprites, animation, collision detection, and user input.)

Animation is one of the most important aspects of a video game. Generally there are two kinds of sprite animation: The first type moves the image from one location to the next, while the second type results in the next frame in a sequence of animations being played.

Moving a sprite is easy to conceptualize, because the sprite is physically transported from one place on the screen to another. The second type of animation is a little less straightforward. When you were a kid, did you ever draw a series of doodles on the corner of each page of a notebook, then flip through the pages with your thumb to make the doodle look like it was alive? If so, that is what we are trying to do when we animate the frames within a sprite — add some life to our game objects.

Some sprites in games use only one type of animation, while some may use both. In our game, we will use the first type of animation for our UFO, and the first and second type of animation for our asteroid. The effect should look like the asteroid is moving and spinning.

Since this is a demo and we want to explain the concept in the most basic terms possible, we won't be implementing any physics or gravity in our game. The ship will move at a constant velocity (for now), while the asteroid will move at a random velocity, which will be initialized whenever the reset button gets pressed. If one of our sprites hits the boundaries of our canvas, we will reverse the x and y, essentially making it "bounce" back into game play (Figure A). Figure A

Illustration of two distinct types of sprite animation.

Animating sprites

This tutorial builds on what we created in part two. However, to really understand what is going on, it helps to see the code in the context of the whole. Therefore, the code listing in the tutorial will be our complete working base, with the new code commented inline. You can follow along with the step-by-step instructions or download and import the entire project into Eclipse.

1. Create a new Android project in Eclipse. Target Android 2.1 or higher. Be sure to rename the startup activity and the corresponding layout to

2. While there are no changes in our manifest file or layout since part two, I've included both below for completeness.


<manifest xmlns:android=""
    android:versionName="1.0" >
       android:targetSdkVersion="15" />
          android:theme="@style/AppTheme" >
                  <action android:name="android.intent.action.MAIN" />
                  <category android:name="android.intent.category.LAUNCHER" />


<LinearLayout xmlns:android=""
    android:orientation="vertical" >
        android:text="ABC's of Android Game Dev" />
               android:text="Reset" />
          android:text="Sprite Speed (?,?)"
          android:id="@+id/the_label" />
        android:text="Last Collision XY (?,?)"
        android:id="@+id/the_other_label" />

3. Use two images in our /res/drawable folder. These are both shown below.

4. Make a few changes to the GameBoard class, which exists in the package com.authorwjf.drawing. The changes are commented inline below and consist of a few key items. First, I have added new private variables; this is so we can set up boundaries for our images in the class. Besides adding new getter/setters, I also modified some of the existing accessors to work with primitives instead of Java's Point class; this is to insure our sprites aren't inadvertently passed by reference and manipulated directly in our controller. Doing so could circumvent our synchronization. Finally, check out the on draw override. We are now using a matrix to perform a rotation on the asteroid; this accomplishes our spinning animation quite easily.

package com.authorwjf.drawing;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.authorwjf.gamedevtut03.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
public class GameBoard extends View{
       private Paint p;
       private List<Point> starField = null;
       private int starAlpha = 80;
       private int starFade = 2;
       //Add private variables to keep up with sprite position and size
       private Rect sprite1Bounds = new Rect(0,0,0,0);
       private Rect sprite2Bounds = new Rect(0,0,0,0);
       private Point sprite1;
       private Point sprite2;
       //Bitmaps that hold the actual sprite images
       private Bitmap bm1 = null;
       private Matrix m = null;
       private Bitmap bm2 = null;

       private int sprite1Rotation = 0;

       private static final int NUM_OF_STARS = 25;
       //Allow our controller to get and set the sprite positions
       //sprite 1 setter
       synchronized public void setSprite1(int x, int y) {
              sprite1=new Point(x,y);
      //sprite 1 getter
      synchronized public int getSprite1X() {
            return sprite1.x;

      synchronized public int getSprite1Y() {
            return sprite1.y;
     //sprite 2 setter
     synchronized public void setSprite2(int x, int y) {
            sprite2=new Point(x,y);
     //sprite 2 getter
     synchronized public int getSprite2X() {
           return sprite2.x;

     synchronized public int getSprite2Y() {
           return sprite2.y;

     synchronized public void resetStarField() {
           starField = null;
    //expose sprite bounds to controller
    synchronized public int getSprite1Width() {
          return sprite1Bounds.width();

    synchronized public int getSprite1Height() {
          return sprite1Bounds.height();

    synchronized public int getSprite2Width() {
          return sprite2Bounds.width();

    synchronized public int getSprite2Height() {
          return sprite2Bounds.height();

    public GameBoard(Context context, AttributeSet aSet) {
           super(context, aSet);
           p = new Paint();
           //load our bitmaps and set the bounds for the controller
           sprite1 = new Point(-1,-1);
           sprite2 = new Point(-1,-1);
           //Define a matrix so we can rotate the asteroid
           m = new Matrix();
           p = new Paint();
           bm1 = BitmapFactory.decodeResource(getResources(), R.drawable.asteroid);
           bm2 = BitmapFactory.decodeResource(getResources(), R.drawable.ufo);
           sprite1Bounds = new Rect(0,0, bm1.getWidth(), bm1.getHeight());
           sprite2Bounds = new Rect(0,0, bm2.getWidth(), bm2.getHeight());

    private void initializeStars(int maxX, int maxY) {
          starField = new ArrayList<Point>();
          for (int i=0; i<NUM_OF_STARS; i++) {
                 Random r = new Random();
                 int x = r.nextInt(maxX-5+1)+5;
                 int y = r.nextInt(maxY-5+1)+5;
                 starField.add(new Point (x,y));
    synchronized public void onDraw(Canvas canvas) {
          canvas.drawRect(0, 0, getWidth(), getHeight(), p);

          if (starField==null) {
                 initializeStars(canvas.getWidth(), canvas.getHeight());
          if (starAlpha>=252 || starAlpha <=80) starFade=starFade*-1;
          for (int i=0; i<NUM_OF_STARS; i++) {
                 canvas.drawPoint(starField.get(i).x, starField.get(i).y, p);
          //Now we draw our sprites.  Items drawn in this function are stacked.
          //The items drawn at the top of the loop are on the bottom of the z-order.
          //Therefore we draw our set, then our actors, and finally any fx.
          if (sprite1.x>=0) {
                 m.postTranslate((float)(sprite1.x), (float)(sprite1.y));
                     canvas.drawBitmap(bm1, m, null);
                     if (sprite1Rotation >= 360) sprite1Rotation=0;
              if (sprite2.x>=0) {
                     canvas.drawBitmap(bm2, sprite2.x, sprite2.y, null);

4. With our game board updating itself, we can move to the /src/ file and set everything in motion. Since part two we've added code to set up our sprite velocity and to calculate the new position with each call back to update the canvas. If it turns out that the new x-y coordinate of a sprite would place it off the screen, we multiply the velocity for that axis by negative one, causing the object to bounce.

package com.authorwjf.gamedevtut03;
import java.util.Random;
import com.authorwjf.drawing.GameBoard;

import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class Main extends Activity implements OnClickListener{
       private Handler frame = new Handler();
       //Velocity includes the speed and the direction of our sprite motion
       private Point sprite1Velocity;
       private Point sprite2Velocity;
       private int sprite1MaxX;
       private int sprite1MaxY;
       private int sprite2MaxX;
       private int sprite2MaxY;
       //Divide the frame by 1000 to calculate how many times per second the screen will update.
       private static final int FRAME_RATE = 20; //50 frames per second
   public void onCreate(Bundle savedInstanceState) {
       Handler h = new Handler();
       //We can't initialize the graphics immediately because the layout manager
       //needs to run first, thus call back in a sec.
       h.postDelayed(new Runnable() {
                     public void run() {
        }, 1000);
        private Point getRandomVelocity() {
              Random r = new Random();
              int min = 1;
              int max = 5;
              int x = r.nextInt(max-min+1)+min;
              int y = r.nextInt(max-min+1)+min;
              return new Point (x,y);

        private Point getRandomPoint() {
              Random r = new Random();
           int minX = 0;
           int maxX = findViewById( -
               int x = 0;
           int minY = 0;
           int maxY = findViewById( -
           int y = 0;
             x = r.nextInt(maxX-minX+1)+minX;
             y = r.nextInt(maxY-minY+1)+minY;
             return new Point (x,y);

    synchronized public void initGfx() {
        //Select two random points for our initial sprite placement.
        //The loop is just to make sure we don't accidentally pick
        //two points that overlap.
        Point p1, p2;
        do {
               p1 = getRandomPoint();
               p2 = getRandomPoint();
        } while (Math.abs(p1.x - p2.x) <
        ((GameBoard)findViewById(, p1.y);
        ((GameBoard)findViewById(, p2.y);
        //Give the asteroid a random velocity
        sprite1Velocity = getRandomVelocity();
        //Fix the ship velocity at a constant speed for now
        sprite2Velocity = new Point(1,1);
        //Set our boundaries for the sprites
        sprite1MaxX = findViewById( -
        sprite1MaxY = findViewById( -
        sprite2MaxX = findViewById( -
        sprite2MaxY = findViewById( -
        frame.postDelayed(frameUpdate, FRAME_RATE);
    synchronized public void onClick(View v) {
 private Runnable frameUpdate = new Runnable() {
      synchronized public void run() {
            //First get the current positions of both sprites
            Point sprite1 = new Point
                              ((GameBoard)findViewById( ;
            Point sprite2 = new Point
            //Now calc the new positions.
            //Note if we exceed a boundary the direction of the velocity gets reversed.
            sprite1.x = sprite1.x + sprite1Velocity.x;
            if (sprite1.x > sprite1MaxX || sprite1.x < 5) {
                    sprite1Velocity.x *= -1;
            sprite1.y = sprite1.y + sprite1Velocity.y;
            if (sprite1.y > sprite1MaxY || sprite1.y < 5) {
                   sprite1Velocity.y *= -1;
            sprite2.x = sprite2.x + sprite2Velocity.x;
            if (sprite2.x > sprite2MaxX || sprite2.x < 5) {
                   sprite2Velocity.x *= -1;
            sprite2.y = sprite2.y + sprite2Velocity.y;
            if (sprite2.y > sprite2MaxY || sprite2.y < 5) {
                   sprite2Velocity.y *= -1;
           ((GameBoard)findViewById(, sprite2.y);
              frame.postDelayed(frameUpdate, FRAME_RATE);

Once again we have our game at a stable point. Better yet, at this juncture, when run on a device or emulator our code is actually starting to look like a video game. Go ahead and see for yourself!

Notice how the asteroid passes right through the ship though? That's a problem. Not to mention it would be nice if you had some control over the saucer and the little alien in the driver's seat. We'll handle both in the final two parts of this game development series, so be sure to check it out.


William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

Editor's Picks