Auto-generate Flow Chart from Java/C++ Codes:

Raptor Flowchart Tutorial For Beginners

Wednesday, June 27, 2012

Beginning Android Game Programming

http://mobile.dzone.com/articles/beginning-android-game


Beginning Android Game Programming

10.01.2010
  Email   Views: 106661

Introduction

Game development on the Android platform is challenging and rewarding and comes with it's own set of pitfalls and hard learned lessons. In this series of articles I hope to show you some of those pitfalls and maybe teach a lesson or two along the way. While you will be challenged I also hope to help you see the rewards that can come from those challenges.

Background

Mobile Java applications are not a new concept and in fact were one of the original focuses of Sun when it because the java project. There have been mobile java games since the first JVM was put on a mobile device. In the case of the Android things are a little bit different. Android uses what is called the Dalvik Virtual Machine, or DVM, which is an opensource implementation of a JVM. There are several differences between Dalvik and a standard JVM, some subtle, some not so subtle. The DVM is also not aligned to either Java SE or Java ME, but to an apache implementation called Apache Harmony Java. All of this makes for a slight learning curve if you happen to be transitioning from Java ME.

Basic Game Architecture

This example requires three basic classes in order to implement a basic game. The game logic can be extended or changed and the resources can be replaces easily using the same patterns. The file DrawablePanel.java contains the code for DrawablePanel which extends SurfaceView and provides a full screen canvas. The DrawablePanel contains an AnimationThread. This class extends Thread (you could also provide a runnable, whatever pattern is more familiar). The AnimationThread class holds a reference to the DrawablePanel described above and updates the DrawablePanel for game logic and forces a redraw of the panel.
 Block Diagram
 

First Steps

I will be using Eclipse as the compilation tool for this tutorial. I am also using the Android Eclipse plugin. Eclipse can be found at www.eclipse.org/downloads and you can find the ADT plugin at developer.android.com/sdk/eclipse-adt.html

First create an Android project:



Since I will be demonstrating an animated sprite implementation we need a sprite strip. Here is the sprite strip we will be using:
  Animated Sprite


You have to add the asset to the project:

  

Code

Now we come to the actual coding. First we will create the AnimatedSprite class since that has the least amount of dependencies.



AnimatedSprite class

01.package com.android.tutorial;
02. 
03.import android.graphics.Bitmap;
04.import android.graphics.Canvas;
05.import android.graphics.Rect;
06. 
07.public class AnimatedSprite {
08.private Bitmap animation;
09.private int xPos;
10.private int yPos;
11.private Rect sRectangle;
12.private int fps;
13.private int numFrames;
14.private int currentFrame;
15.private long frameTimer;
16.private int spriteHeight;
17.private int spriteWidth;
18. 
19.public AnimatedSprite() {
20.sRectangle = new Rect(0000);
21.frameTimer = 0;
22.currentFrame = 0;
23.xPos = 80;
24.yPos = 200;
25.}
26. 
27.public void Initialize(Bitmap bitmap, int height, int width, int fps,int frameCount) {
28.this.animation = bitmap;
29.this.spriteHeight = height;
30.this.spriteWidth = width;
31.this.sRectangle.top = 0;
32.this.sRectangle.bottom = spriteHeight;
33.this.sRectangle.left = 0;
34.this.sRectangle.right = spriteWidth;
35.this.fps = 1000 / fps;
36.this.numFrames = frameCount;
37.}
38. 
39.public int getXPos() {
40.return xPos;
41.}
42. 
43.public int getYPos() {
44.return yPos;
45.}
46. 
47.public void setXPos(int value) {
48.xPos = value;
49.}
50. 
51.public void setYPos(int value) {
52.yPos = value;
53.}
54. 
55.public void Update(long gameTime) {
56.if( gameTime > frameTimer + fps) {
57.frameTimer = gameTime;
58.currentFrame += 1;
59. 
60.if( currentFrame >= numFrames ) {
61.currentFrame = 0;
62.}
63. 
64.sRectangle.left = currentFrame * spriteWidth;
65.sRectangle.right = sRectangle.left + spriteWidth;
66.}
67.}
68. 
69.public void draw(Canvas canvas) {
70.Rect dest = new Rect(getXPos(), getYPos(), getXPos() + spriteWidth,
71.getYPos() + spriteHeight);
72.canvas.drawBitmap(animation, sRectangle, dest, null);
73.}
74.}

Here we can see that AnimateSprite is a fairly simple class. It basically keeps track of what frame it's currently displaying and increments it during update if the appropriate time has elapsed. If it reaches the end of the list it will wrap around and begin rendering at the start of the list again.
I've abstracted the interaction between the animation logic and display logic with an interface called ISurface. Let's look at that interface now.



ISurface interface

01.package com.android.tutorial;
02. 
03.import android.graphics.Canvas;
04. 
05.public interface ISurface {
06.void onInitalize();
07.void onDraw(Canvas canvas);
08.void onUpdate(long gameTime);
09.}

Here we can see that the ISurface interface provides some basic functions. One for initialization which is called at startup or when the sprite is first initialized. The second is the draw function which is pretty self explanatory. The final function is the update function which is what is used by the animation thread to update it's animation(s).

Let's look at the AnimationThread now


AnimationThread class

01.package com.android.tutorial;
02. 
03.import android.graphics.Canvas;
04.import android.view.SurfaceHolder;
05. 
06.public class AnimationThread extends Thread {
07.private SurfaceHolder surfaceHolder;
08.private ISurface panel;
09.private boolean run = false;
10. 
11.public AnimationThread(SurfaceHolder surfaceHolder, ISurface panel) {
12.this.surfaceHolder = surfaceHolder;
13.this.panel = panel;
14. 
15.panel.onInitalize();
16.}
17. 
18.public void setRunning(boolean value) {
19.run = value;
20.}
21. 
22.private long timer;
23. 
24.@Override
25.public void run() {
26. 
27.Canvas c;
28.while (run) {
29.c = null;
30.timer = System.currentTimeMillis();
31.panel.onUpdate(timer);
32. 
33.try {
34.c = surfaceHolder.lockCanvas(null);
35.synchronized (surfaceHolder) {
36.panel.onDraw(c);
37.}
38.finally {
39.// do this in a finally so that if an exception is thrown
40.// during the above, we don't leave the Surface in an
41.// inconsistent state
42.if (c != null) {
43.surfaceHolder.unlockCanvasAndPost(c);
44.}
45.}
46.}          
47.}
48.}
This class isn't doing much either, but what it is doing is very important. This class extends Thread. You could also create a runnable and implement threading that way. This way is more obvious which is why I chose it. The run function of this class is where all the work is being done. In this case the function retrieves the current system time and calls it's dependent panel's update function. It then obtains an exclusive lock on the containing surface's canvas and passes that canvas to the surface view's draw function. This call is synchronized to ensure stability.

Now we need to provide a concrete implementation of the ISurface interface for our tutorial. This will be the responsibility of the DrawablePanel class. Let's look at that class now:


DrawablePanel class

01.package com.android.tutorial;
02. 
03.import android.content.Context;
04.import android.graphics.Bitmap;
05.import android.graphics.BitmapFactory;
06.import android.graphics.Canvas;
07.import android.graphics.Color;
08.import android.view.SurfaceHolder;
09.import android.view.SurfaceView;
10. 
11.public abstract class DrawablePanel
12.extends SurfaceView
13.implements SurfaceHolder.Callback, ISurface {
14. 
15.private AnimationThread thread;
16. 
17.public DrawablePanel(Context context) {
18.super(context);
19.getHolder().addCallback(this);
20. 
21.this.thread = new AnimationThread(getHolder(), this);
22.}
23. 
24.@Override
25.public void onDraw(Canvas canvas) {
26.}
27. 
28.@Override
29.public void surfaceChanged(SurfaceHolder holder, int format, int width,
30.int height) {
31.}
32. 
33.@Override
34.public void surfaceCreated(SurfaceHolder holder) {
35.thread.setRunning(true);
36.thread.start();
37.}
38. 
39.@Override
40.public void surfaceDestroyed(SurfaceHolder holder) {
41.boolean retry = true;
42.thread.setRunning(false);
43.while (retry) {
44.try {
45.thread.join();
46.retry = false;
47.catch (InterruptedException e) {
48.// we will try it again and again...
49.}
50.}          
51.}
52.}
 
This class extends SurfaceView and implements the SurfaceHolder.Callback interface. This is one technique to create a custom view. This class also implements our ISurface interface. It also has an AnimationThread. During construction the class does some standard plumbing to hookup the SurfaceHolder.Callback interface and starts the AnimationThread we looked at earlier. One of the parameters of the AnimationThread is an ISurface. This is where we can hook up our update and draw events to the animation thread. In this case we don't have to perform any logic for either event so they are left as empty functions. The final point of interest is surface creation or destruction. This will occur if your app goes out of focus for any reason. This could be because the user hit the home button, the app is being put to sleep, or the operation system is cleaning up memory for a larger foreground app. There are other reasons but those are the most common. In this case we have to make sure the thread no longer pumps animations and tries to draw to the canvas since the canvas will no longer exist. This is done by stopping the thread in the surfaceDestroyed callback. The thread is started in the surfaceCreated callback. This is the main reason for implementing the SurfaceHolder.Callback interface.

Finally edit the main activity class AndroidTutorial.java and replace the code with the following:

AndroidTutorial activity
01.package com.android.tutorial;
02. 
03. 
04.import android.app.Activity;
05.import android.content.Context;
06.import android.graphics.BitmapFactory;
07.import android.graphics.Canvas;
08.import android.os.Bundle;
09. 
10.public class AndroidTutorial extends Activity {
11.AnimatedSprite animation = new AnimatedSprite();
12. 
13./** Called when the activity is first created. */
14.@Override
15.public void onCreate(Bundle savedInstanceState) {
16.super.onCreate(savedInstanceState);
17.setContentView(new AndroidTutorialPanel(this));
18.}
19. 
20.class AndroidTutorialPanel extends DrawablePanel {
21. 
22.public AndroidTutorialPanel(Context context) {
23.super(context);
24.}
25. 
26.@Override
27.public void onDraw(Canvas canvas) {
28.super.onDraw(canvas);
29.AndroidTutorial.this.animation.draw(canvas);
30.}
31. 
32.@Override
33.public void onInitalize() {
34.AndroidTutorial.this.animation.Initialize(
35.BitmapFactory.decodeResource(
36.getResources(),
37.R.drawable.explosion),
38.3232147);
39.}
40. 
41.@Override
42.public void onUpdate(long gameTime) {
43.AndroidTutorial.this.animation.Update(gameTime);
44.}
45.}
46. 
47.}
In this class we create an inner class called AndroidTutorialPanel which extends the DrawablePanel class we looked at above. Since DrawablePanel is an ISurace AndroidTutorialPanel overrides the ISurface members and provides custom implementation. In this case the class creates, updates, and draws an instance of an AnimatedSprite which we looked at previously. One point of interest is the creation of the sprite. The constructor expects a resource id, a resource height and width, and finally some timing values. These values control how many times per second the animation is drawn and how many frames to draw. You can use this to create many animations from a single sprite sheet and customize how fast those animations run.

Conclusion

You can easily extend this framework with your own panels or animation threads even. You can even customize your animation sprites how you see fit such as adding velocity, acceleration, or even a complete physics framework. There is no limit accept your own imagination.
Here is a screen shot of the demo in action:
AttachmentSize
Block Diagram.img_assist_custom-300x388.png27.82 KB
New_Android_Project-09.27.2010-09.20.05PM.png42.79 KB
explosion.png3.73 KB
SS-09.27.2010-09.27.45PM.jpg17.42 KB
New_Java_Class-09.27.2010-09.34.44PM.img_assist_custom-300x359.png46.99 KB
New_Java_Interface-09.27.2010-09.37.16PM.img_assist_custom-300x299.png73.09 KB
New_Java_Class-09.27.2010-09.41.27PM.img_assist_custom-300x359.png47.02 KB
New_Java_Class-09.27.2010-09.39.59PM.img_assist_custom-300x359.png46.96 KB
5554GPSTester-09.29.2010-08.52.43AM.img_assist_custom-300x213.png36.6 KB
AndroidGameTutorial.zip

No comments: