I'm a 27 year old software developer that mainly focuses on software development. As everyone i have taken a side in the Apple vs Android game. My choice is Android because of oh so many reasons :-) By contributing to the Android developers content online ( in the form of writing Android tutorials ), I hope to convince others to make the same decision and help them make nice content for the Android platform. Mark is a DZone MVB and is not an employee of DZone and has posted 20 posts at DZone. You can read more from them at their website. View Full User Profile

Android tutorial: How to paint, animate, loop and remove a sprite!

09.21.2011
| 26309 views |
  • submit to reddit

Last week I posted a story about painting on a canvas. Today I want to elaborate on that and talk about sprites. A sprite is a two-dimensional image or animation that is integrated into a larger scene. Initially used to describe graphical objects handled separately of the memory bitmap of a video display, the term has since been applied more loosely to refer to various manner of graphical overlays.

In this tutorial I will show you:

  • how to paint a sprite onto the canvas with the onTouch event.
  • how to make an AnimatedSprite class
  • Extent the AnimatedSprite class to loop the Sprite

You can download the Eclipse source on the bottom of the page.

The base of the Android game development tutorial for today is from a Sample app from the Android developers: LunarLander. I stripped away all useless things and went from there.

If you want to learn more about the basics check out these posts:

 

For this tutorial I used this png file:

Sprite transparent explosion

Resource: Explosion sprite with transparency

I'll paste in the class that puts the Sprite in the scene:

package com.pxr.gamebase;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;

public class AnimatedSprite {
	private Bitmap animation;
	private int xPos;
	private int yPos;
	private Rect sRectangle;
	private int fps;
	private int numFrames;
	private int currentFrame;
	private long frameTimer;
	private int spriteHeight;
	private int spriteWidth;
	private boolean loop;
	public boolean dispose;

	public AnimatedSprite() {
		sRectangle = new Rect(0, 0, 0, 0);
		frameTimer = 0;
		currentFrame = 0;
		xPos = 80;
		yPos = 200;
		dispose = false;
	}

	public void Initialize(Bitmap bitmap, int height, int width, int fps, int frameCount, boolean loop) {
		this.animation = bitmap;
		this.spriteHeight = height;
		this.spriteWidth = width;
		this.sRectangle.top = 0;
		this.sRectangle.bottom = spriteHeight;
		this.sRectangle.left = 0;
		this.sRectangle.right = spriteWidth;
		this.fps = 1000 / fps;
		this.numFrames = frameCount;
		this.loop = loop;
	}

	public int getXPos() {
		return xPos;
	}

	public int getYPos() {
		return yPos;
	}

	public void setXPos(int value) {
		xPos = value - (spriteWidth/2);
	}

	public void setYPos(int value) {
		yPos = value - (spriteHeight/2);
	}

	public void Update(long gameTime) {
		if( gameTime > frameTimer + fps) {
			frameTimer = gameTime;
			currentFrame += 1;

			if( currentFrame >= numFrames ) {
				currentFrame = 0;

				if(!loop) dispose = true;
			}

			sRectangle.left = currentFrame * spriteWidth;
			sRectangle.right = sRectangle.left + spriteWidth;
		}
	}

	public void draw(Canvas canvas) {
		Rect dest = new Rect(getXPos(), getYPos(), getXPos() + spriteWidth,
										getYPos() + spriteHeight);
		canvas.drawBitmap(animation, sRectangle, dest, null);
	}
}

This class contains all the needed methods to create & animate the Sprite. The most important method's are the Initialize, Update and Draw.

Now we need to utilize this class in our Android game application. Below is the doTouch method from my AnimationView class:

public void doTouch(MotionEvent event){

        	int action = event.getAction();
            float x = event.getX();  // or getRawX();
            float y = event.getY();

            switch(action){
            case MotionEvent.ACTION_DOWN:
            	if(mMode != STATE_RUNNING)
            		setState(STATE_RUNNING);
            	else if(mMode == STATE_RUNNING){

            		AnimatedSprite a = new AnimatedSprite();

            		if(b == null)
            			b = BitmapFactory.decodeStream(res.openRawResource(R.drawable.explosion));

                    a.Initialize(b, 120, 160, 24, 20, true);

            		a.setXPos((int)x);
            		a.setYPos((int)y);  

            		synchronized(mSprites){
            			mSprites.add(a);
            		}
            	}
            	break;
            }
        }

This method adds a new AnimatedSprite to the collection that will be drawn. And it sets to position to the position of where Android detected a Touch event.

If you dont want the sprites to loop set the last property of a.Initialize to false

My game loop calls 2 methods: doDraw & updatePhysics. The updatePhysics updates the sprites. The other just paints the Sprites.

updatePhysics:

private void updatePhysics() {
            long now = System.currentTimeMillis();

            // Do nothing if mLastTime is in the future.
            // This allows the game-start to delay the start of the physics
            // by 100ms or whatever.
            if (mLastTime > now) return;

            if (mLastTime != 0) {
        		int time = (int) (now - mLastTime);
        		frameSampleTime += time;
        		frameSamplesCollected++;
        		if (frameSamplesCollected == 10) {
	        		fps = (int) (10000 / frameSampleTime);
	        		frameSampleTime = 0;
	        		frameSamplesCollected = 0;
        		}
        	}

            synchronized(mSprites){
	            for( AnimatedSprite a : mSprites){
	            	a.Update(now);

	            	if(a.dispose)
	            		mSpritestoRemove.add(a);
	            }
            }

            synchronized(mSpritestoRemove){
	            mSprites.removeAll(mSpritestoRemove);
	            mSpritestoRemove.clear();
            }

            numSprites = mSprites.size();
            mLastTime = now;

        }

doDraw:

private void doDraw(Canvas canvas) {
            // Draw the background image. Operations on the Canvas accumulate
            // so this is like clearing the screen.
            //canvas.drawBitmap(mBackgroundImage, 0, 0, null);
        	canvas.drawColor(Color.BLACK);

        	for( AnimatedSprite a : mSprites){
        		a.draw(canvas);
        	}       		

        	if(mMode == STATE_RUNNING ){
	        	canvas.drawText(fps + " fps", getWidth() - 60, getHeight() - 40, textPaint);
	        	canvas.drawText(numSprites + " sprites", getWidth() - 60, getHeight() - 20, textPaint);
        	}

        	canvas.restore();
        }

Do you have a question or post request about Android game development ? Just leave a comment and ill get to it :-)

Feel free to download & explore the Eclipse project below.



References
Published at DZone with permission of Mark Mooibroek, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Gagan Peter replied on Sat, 2012/04/14 - 4:40am

I keep getting force closes. I copied the AnimationView from the downloadable folder, however there were some errors on like five if/else statements with strings. The app loads fine, however when I click, the FPS and Sprite counter show up, but then it force closes.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.