Skip to main content

J2ME Tutorial, Part 3: Exploring the Game API of MIDP 2.0

July 7, 2005

{cs.r.title}









Contents
J2ME Gaming API: An Overview
A Very Short Primer on Game Building
Building a J2ME Game: Start with the GameCanvas
Defining Game Characteristics
Building a J2ME Game: Creating Backgrounds
Using the TiledLayer class
Building a J2ME Game: Sprites and LayerManager
   Managing Layers Using the LayerManager
Adding More Sprites and Detecting Collisions
Conclusion

J2ME is a popular platform for developing games for wireless devices. With MIDP 2.0, a new package has been introduced that provides several gaming constructs that would only have been possible in MIDP 1.0 with a great deal of repetitive
code. In this part of this tutorial series, I will introduce you to this gaming package
and help you develop a simple game that uses all of the classes of this package as a learning tool. The package is called javax.microedition.lcdui.game,
and it builds upon the concepts that you learned in the previous installments in this series.

J2ME Gaming API: An Overview

There are only five classes in the javax.microedition.lcdui.game
package: GameCanvas, Layer, Sprite, TiledLayer,
and LayerManager. These five classes are enough to provide a platform
for the development of games with a wide range of capabilities.

The Layer class is the superclass of the Sprite and TiledLayer
classes. This class abstracts the behavior of a visual element in a game. This
element can be a sprite, which represents an independent graphic (or a collection
of graphics for animation) that can be moved around the game screen, or a tiled layer,
which represents a graphic that can be used to create vast game backgrounds
with only a handful of images. You use the
Layer classes for positioning and visibility. The subclasses override the

paint(Graphics 
  g)
method, which has the task of rendering the elements on the screen.

The LayerManager class provides a convenient mechanism to manage the
various visual elements of a game (sprites and tiled layers) by rendering the proper layer in the proper order.

The GameCanvas class is made useful by extending the functionality
of the Canvas class (from the previous article).
It provides an off-screen buffer, to which all rendering operations are done
before flushing them on the device screen. It also provides an easy-to-use mechanism
to query the current keys being pressed by the user.

The best way to introduce you to these classes is with the help of a working game example, which we will build from the ground up, explaining the various
facets of a game. This is helpful whether or not you have programmed a game before, if you are looking to learn
how to do it for the wireless devices using J2ME's gaming API. After a quick introduction to game building (for those who have never created a game), the remaining sections introduce each of the classes from the javax.microedition.lcdui.game package, with the help
of a concise but complete game.

A Very Short Primer on Game Building

A game or animation is built according to the principle of repetitively executing
a piece of code. This piece of code tracks the value of instance variables and
updates the game state accordingly. Based on the game state, the code then draws/paints/repaints
the game screen with the elements that make up the game. The values of the instance
variables may change because of either user interaction or internal game behavior.

The repetitive execution is effected by putting the repetitive code in an infinite
loop. Before entering the loop, an instance variable may be checked to see if
the game should still be running, and if not, the loop may be exited. The code
in the loop should allow the current thread of execution to sleep every few
milliseconds to control the rate at which the update to the instance variables
is done (in effect, how fast the game screen should be refreshed).

To put it in coding terms:

// main class
public MainClass {
  private GameCanvas canvas = new MyGameCanvas();


  public MainClass() {
    // start a thread that will run infinitely
canvas.start();
  }
 
  // rest of the code
}


// the actual drawing class
public MyGameCanvas extends GameCanvas implements Runnable {


  public MyGameCanvas() {
    // instantiation code
  }
 
  public void start() {
    // do initialization

// and then start a thread
Thread runner = new Thread(this);
runner.start();
  }
 
  private void run() {
 
    // or while(keeprunning = true)
    // where keeprunning is an instance variable

    while(true) {


      // checks if the game has reached
      // some boundary states or special conditions
  verifyGameState();


      // gets input from user and
      // updates instance variables
      // that describe the games elements

    checkUserInput();


      // paints elements on screen to reflect
      // the current game state using the current
      // graphics object

  updateGameScreen(getGraphics());


      // control the rate at which screen updates are done

  Thread.sleep(milliseconds);
}
  }
}

We will use this structure to develop a game in the following sections.







Building a J2ME Game: Start with the GameCanvas

A GameCanvas class is a specialized subclass of the Canvas
class that you encountered in the previous
installment of
this series. GameCanvas is optimized for gaming because it provides
a special off-screen buffer in which all painting operations are done. When all painting on this buffer is complete, the buffer is rendered on the device
screen by calling the flushGraphics() method. This double buffering
has the advantage of producing smooth transitions of moving elements on a screen
to counter the flickering that might happen if no buffering were provided. The size
of the buffer is equal to the size of the device screen, and there is only one
buffer per GameCanvas instance.

The GameCanvas class provides a storage
mechanism for the state of game keys, which is a useful way to query user interaction
with the game. This provides a simple way of keeping track of the number of
times the user has pressed a particular key. Calling the method getKeyStates()
returns a bitwise representation of all of the physical game keys, expressed as
1 for pressed and 0 for unpressed, since the last time the method was called.
Only the following game states are identified, which is what you would expect,
keeping in mind the game keys defined by the Canvas
class: DOWN_PRESSED, UP_PRESSED, RIGHT_PRESSED, LEFT_PRESSED, FIRE_PRESSED,
GAME_A_PRESSED, GAME_B_PRESSED, GAME_C_PRESSED, and GAME_D_PRESSED.

Let's build a game canvas by extending the GameCanvas class. Listing
1 shows the first attempt, while Listing 2 shows the MIDlet that will be used
to run the examples.

Please follow the instructions given in part one of
this series, which explain how to create, test, and run a MIDlet using the
Wireless toolkit.

package com.j2me.part3;

import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;

import java.io.IOException;

public class MyGameCanvas
  extends GameCanvas implements Runnable {

public MyGameCanvas() {
  super(true);
}

public void start() {

  try {

    // create and load the couple image
// and then center it on screen when
// the MIDlet starts
coupleImg = Image.createImage("/couple.gif");
coupleX = CENTER_X;
coupleY = CENTER_Y;

  } catch(IOException ioex) { System.err.println(ioex); }

  Thread runner = new Thread(this);
  runner.start();

}

public void run() {

  // the graphics object for this canvas
  Graphics g = getGraphics();

  while(true) { // infinite loop

      // based on the structure

  // first verify game state
  verifyGameState();

  // check user's input
  checkUserInput();

  // update screen
  updateGameScreen(getGraphics());

// and sleep, this controls
// how fast refresh is done
try {
  Thread.currentThread().sleep(30);
} catch(Exception e) {}

  }

}

private void verifyGameState() {
  // doesn't do anything yet
}

private void checkUserInput() {

  // get the state of keys
  int keyState = getKeyStates();

  // calculate the position for x axis
  calculateCoupleX(keyState);

}

private void updateGameScreen(Graphics g) {

  // the next two lines clear the background
  g.setColor(0xffffff);
  g.fillRect(0, 0, getWidth(), getHeight());

  // draws the couple image according to current
  // desired positions
  g.drawImage(
    coupleImg, coupleX,
coupleY, Graphics.HCENTER | Graphics.BOTTOM);

  // this call paints off screen buffer to screen
  flushGraphics();

}

private void calculateCoupleX(int keyState) {

  // determines which way to move and changes the
  // x coordinate accordingly
  if((keyState & LEFT_PRESSED) != 0) {
    coupleX -= dx;
  }
  else if((keyState & RIGHT_PRESSED) != 0) {
    coupleX += dx;
  }

}

// the couple image
private Image coupleImg;

// the couple image coordinates
private int coupleX;
private int coupleY;

// the distance to move in the x axis
private int dx = 1;

// the center of the screen
public final int CENTER_X = getWidth()/2;
public final int CENTER_Y = getHeight()/2;

}

Listing 1. MyGameCanvas: A first attempt at building a gaming canvas

Listing 2 shows the MIDlet that will use this gaming canvas:

package com.j2me.part3;

import javax.microedition.midlet.MIDlet;
import javax.microedition.lcdui.Display;

public class GameMIDlet extends MIDlet {

MyGameCanvas gCanvas;

public GameMIDlet() {
gCanvas = new MyGameCanvas();
}

public void startApp() {
Display display = Display.getDisplay(this);
gCanvas.start();
display.setCurrent(gCanvas);
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}

Listing 2. MIDlet class to run the game examples

Using both of these classes, create a project with your Toolkit (I have called
this project "Part 3 Game Examples") and then build and run the project.
You will need this image file: couple.gif, named couple.gif,
in the res folder of your project, or you can use a similar-sized image. Figure 1 shows the expected output.

Figure 1
Figure 1. Building a game: using GameCanvas

The solitary image in the middle of the screen can be moved left and right
with the help of the left and right game keys, respectively. In the code shown in
Listing 1, this is achieved by querying the game states in the checkUserInput()
method and then calling the calculateCoupleX() method with this
game state. As you can see, by bit-wise ORing the state with the supplied Constants
in the GameCanvas class, you can easily determine which key the
user has pressed and act accordingly. The x axis position of the image is moved
left or right of the current position by adding or subtracting delta x (dx)
from it.

The image is rendered on the screen in the updateGameScreen()
method. This method is passed the current Graphics object. This
object is created for you by the GameCanvas class, and there is
only one such object per GameCanvas. The method clears this graphics
buffer of any previous renderings, draws the couple image based on the current
coupleX variable (and the currently unchanging coupleY
variable) and then flushes this buffer on the device screen.

The infinite loop in the run() method follows the game structure
that I described in the sidebar earlier. This loop sleeps for 30 milliseconds
before going on another cycle to determine the user input and refresh the buffer.
You can experiment with this value to slow down or speed up the refresh rate.

Finally, notice that the MyGameCanvas constructor calls its superclass
GameCanvas's constructor with a parameter value of true. This indicates
that the normal key event mechanism, inherited from the Canvas
class, should be suppressed, as this code does not require these notifications.
The game state is adequately handled by the key state information, which is
fetched from the getKeyStates() method. By suppressing the notification
mechanism for "key pressed," "key released," and "key repeated," the game performance
is improved.







Defining Game Characteristics

A game where all you have to do is to move the central character left and right
is not very fun. Let's make some modifications to the game skeleton in Listing
1 to define this game a little better. To start with, specify a boundary for your game. It is essential to do this,
because it helps to make your game consistently sized across different devices.
To do this, start by defining some constants that are shown in the code here:

// the game boundary
public static final int GAME_WIDTH = 160;
public static final int GAME_HEIGHT = 160;

// the shifted x,y origin of the game
public final int GAME_ORIGIN_X = (getWidth() - GAME_WIDTH)/2;
public final int GAME_ORIGIN_Y = (getHeight() - GAME_HEIGHT)/2;

// the height of sections below and above the couple
public final int SECTION_HEIGHT = 64;

// the base on which the couple will move
public final int BASE = GAME_ORIGIN_Y + GAME_HEIGHT - SECTION_HEIGHT;

// the max height the couples can jump
public final int MAX_HEIGHT = 32;

(Note that I have introduced a game characteristic that indicates that this
couple may soon be jumping on the screen, with the help of the MAX_HEIGHT constant.)
On the screen, these constants help define the boundary of the game and its sole
element (the couple), as shown in Figure 2.

Figure 2
Figure 2. Defining the game boundaries using the game constants

Of course, now you need to modify the rest of the code to use these constants.
Add a new method to Listing 1 called buildGameScreen(Graphics g),
as shown in code here:

private void buildGameScreen(Graphics g) {

  // set the drawing color to black
  g.setColor(0x000000);

  // draw the surrounding rectangle
  g.drawRect(GAME_ORIGIN_X, GAME_ORIGIN_Y, GAME_WIDTH, GAME_HEIGHT);

  // draw the base line
  g.drawLine(GAME_ORIGIN_X, BASE, GAME_ORIGIN_X + GAME_WIDTH, BASE);

  // draw the maximum line to where the couple can jump to
  g.drawLine(GAME_ORIGIN_X, BASE - MAX_HEIGHT,
    GAME_ORIGIN_X + GAME_WIDTH, BASE - MAX_HEIGHT);

}

Also add a call to this method in the updateGameScreen() method,
before the couple image is drawn. The game boundaries have been defined and
the only thing left to do is to make the starting position for the couple image
as the BASE and not CENTER_Y. Change this in the start() method
by setting coupleY = BASE;.

The couple image can move left and right with the left and right game keys,
but now we ensure that it does not move past the game boundary. This was a problem in Listing
1, too, but in that case, the image simply vanished off the screen, as the boundary
was the edge of the screen. It will look very odd if the image went past the
boundaries now. Therefore, modify the left and right press actions in the
calculateCoupleX() method to restrict movement beyond the boundaries.
This modified method is listed here:

private void calculateCoupleX(int keyState) {

  // determines which way to move and changes the
  // x coordinate accordingly
  if((keyState & LEFT_PRESSED) != 0) {
    coupleX =
      Math.max(
        GAME_ORIGIN_X + coupleImg.getWidth()/2,
        coupleX - dx);
  }
  else if((keyState & RIGHT_PRESSED) != 0) {
    coupleX =
      Math.min(
        GAME_ORIGIN_X + GAME_WIDTH - coupleImg.getWidth()/2,
        coupleX + dx);;
  }

}

This method now uses Math.max() and Math.min() methods
to restrict the couple image within the game boundaries. Notice that it also
incorporates the width the of the image in these calculations.

I spoke earlier about making the couple image jump around on the screen. Let's
see how this can be achieved by adding a method to move the image along the Y axis,
independently of the user playing the game.

Add three new instance variables to Listing 1, called up, jumpHeight,
and random, as shown here:

// a flag to indicate which direction the couple are moving
private Boolean up = true;

// indicates the random jump height, calculated for every jump
private int jumpHeight = MAX_HEIGHT;

// random number generator
public Random random = new Random();

As you can see, jumpHeight is initialized to MAX_HEIGHT. This
jumpHeight variable will be calculated for each jump that the couple
make and it will be set to a random value each time. This is shown in the calculateCoupleY()
method shown here:

private void calculateCoupleY(int keyState) {

  // check if the couple were on the way up
  if(up) {

    // if yes, see if they have reached the current jump height
    if((coupleY > (BASE - jumpHeight + coupleImg.getHeight()))) {

      // if not, continue moving them up
  coupleY -= dy;
} else if(coupleY == (BASE - jumpHeight + coupleImg.getHeight())) {

  // if yes, start moving them down
  coupleY += dy;

  // and change the flag
  up = false;

}

  } else {

    // the couple are on their way down, have they reached base?
    if(coupleY < BASE) {

      // no, so keep moving them down
  coupleY += dy;

} else if(coupleY == BASE) {

  // have reached base, so calculate a new
  // jump height which is not more than MAX_HEIGHT
  int hyper = random.nextInt(MAX_HEIGHT + 1);

  // but make sure that this it is atleast greater than the image height
  if(hyper > coupleImg.getHeight()) jumpHeight = hyper;

  // move the image up
  coupleY -= dy;

  // and reset the flag
  up = true;

}
  }
}

Note that since this method doesn't depend on the user pressing the up or down
game keys, it has no use for the keyState information. But this
value is passed to it nonetheless, in order to maintain conformity with the calculateCoupleX()
method. This method starts moving the couple image by changing the coupleY
variable in the upwards direction until it reaches the current jump height (which
is the MAX_HEIGHT at starting). Once it reaches this jump height, it starts
moving it in the opposite direction until it reaches BASE. At this point, a new
jump height value, between the MAX_HEIGHT and couple image heights, is randomly
calculated and the couple start jumping again.

The overall effect is of a randomly jumping couple who can be moved left and
right by the user playing the game. A snapshot is shown in Figure 3.

Figure 3
Figure 3. Game snapshot







Building a J2ME Game: Creating Backgrounds Using the TiledLayer Class

In this section, you will add some color to the game by providing a background
using the TiledLayer class. The game is divided into three sections:
the top section can be thought of as the sky, the middle section in which the
couple jump is the earth, and the bottom section is the sea. These three sections
can be filled easily using three colored images of the size 32 by 32 pixels each, one
for each section. However, each section is bigger than 32 by 32 pixels, and the TiledLayer
class is used to define large areas like these with small images.

To start, divide the game screen into squares of 32 by 32 each and number each
row and column, starting with an index of 0. This is shown in Figure 4 and results
in a 5-by-5-cell background.

Figure 4
Figure 4. Divide the game screen into individual cells

Thus, cells (0, 0) to (1, 4) are to painted with a sky image; cells (2, 0)
to (2, 4) are to be painted with an earth image, and cells (3, 0) to (4, 4) are
to be painted with a sea image. You will do this with the image shown in Figure
5.

Figure 5
Figure 5. Background image

The first 32 by 32 cell represents the earth image, the second represents
the sea, and the last represents the sky. When you use the TiledLayer
class, these images are numbered starting from index 1 (not 0; therefore, earth is 1,
sea is 2, and sky is 3). The TiledLayer class will take this one image
and divide it into three separate images used for rendering the game background.
In our case, we want the TiledLayer class to render a 5-by-5-cell
background using cells of 32 by 32 pixels each. This is achieved by the following
code:

// load the image
backgroundImg = Image.createImage("/tiledLayer1.gif");

// create the tiledlayer background
background = new TiledLayer(5, 5, backgroundImg, 32, 32);

As you can see, the first two parameters to the TiledLayer constructor
represent the total background size, the next parameter represents the image,
and the last two parameters represents the size of each cell. This size will
be used by the TiledLayer class to carve the image into its individual
background cells.

All that is now left is to set each cell with its respective image.
The full code to create the background is listed below in a method called createBackground().
You will need to add a call to this method from the start() method
of the MyGameCanvas class. Once this is done, add a call to paint
this background using background.paint(g) at the end of the buildGameScreen()
method, which will render it to screen.

// creates the background using TiledLayer
private void createBackground() throws IOException {

  // load the image
  backgroundImg = Image.createImage("/tiledlayer1.gif");

  // create the tiledlayer background
  background = new TiledLayer(5, 5, backgroundImg, 32, 32);

  // array that specifies what image goes where
  int[] cells = {
    3, 3, 3, 3, 3, // sky
    3, 3, 3, 3, 3, // sky
    1, 1, 1, 1, 1, // earth
    2, 2, 2, 2, 2, // sea
    2, 2, 2, 2, 2  // sea
  };

  // set the background with the images
  for (int i = 0; i < cells.length; i++) {
    int column = i % 5;
    int row = (i - column)/5;
    background.setCell(column, row, cells[i]);
  }

  // set the location of the background
  background.setPosition(GAME_ORIGIN_X, GAME_ORIGIN_Y);
 
}

The final result will look like Figure 6.

Figure 6
Figure 6. Game with a background added







Building a J2ME Game: Sprites and LayerManager

So far, I have used an image of a jumping couple to showcase a game element.
This element is currently rendered as an image; but it makes sense to render it as a sprite
instead. A sprite is a term in gaming parlance that refers to any visual element,
usually an animated image, that can be moved around the screen independently of
the other elements. The Sprite class is used to represent sprites
in the MIDP 2.0 gaming API. This class provides methods to animate the sprite
based on a handful of images, similar to the way backgrounds are created using
the TiledLayer class. More importantly, it provides methods
to check collisions with other game elements, including images, sprites, or tiled
layers.

Let's start by converting the existing couple image into a sprite. To showcase
animation, I will use the image showed in Figure 7, which is the couple image
duplicated over with a different color into two different frames, each
10 by 10 pixels each.

Figure 7
Figure 7. Frames for couple Sprite animation

Similar to the TiledLayer class, the Sprite class
requires that the size of each frame be passed in to its constructor. This is
shown here:

coupleSprite = new Sprite(coupleImg, 10, 10);

This code, added after the creation of the couple image in the start()
method, creates a couple sprite with two frames of 10 by 10 pixels each, numbered
from 0 onwards. Thus, to alternate between the sprite images, you can call the
nextFrame() method, which gets the next image in the current sequence. Since there are only two images in this sprite sequence, they will be shown
one after another. If you want to make a particular frame/image the current
displayable image for a sprite in a longer frame sequence, you can do so by using
the method setFrame(int sequenceNo). In this case, add coupleSprite.nextFrame()
in the updateGameScreen() method.

You now don't want the couple image to be painted on the screen. Before the couple sprite can be painted on the
screen, you need to define a reference pixel for it. Think of this as an origin around
which all painting operations are done. By default, a sprite is painted with
its upper left corner as its origin. Similar to the way you set the reference
of the couple image using the Graphics.HCENTER | Graphics.BOTTOM code, you need
to define a reference pixel for the sprite. This is shown here:

coupleSprite.defineReferencePixel(coupleSprite.getWidth()/2, coupleSprite.getHeight());

Add this snippet after the creation of the sprite as described earlier. Now,
instead of positioning the sprite based on its original origin, you will position
it based on this reference pixel as, shown here:

coupleSprite.setRefPixelPosition(coupleX, coupleY);
coupleSprite.paint(g);

The last line in this code snippet paints the sprite on the graphics object
that is passed to it. As expected, you will need to insert these lines in the
updateGameScreen() method in lieu of the lines that painted the
couple image. The final result will look exactly the same as before, except
the jumping couple will be replaced with a flickering jumping couple!

Before going forward, make sure that you change all references to the coupleImg
variable to coupleSprite in thecalculateX() and calculateY() methods.

Managing Layers Using the LayerManager

Recall that both the Sprite and TiledLayer classes
extend the Layer class. A game may contain at least one TiledLayer
and several Sprite classes. With so many layers to control,
the LayerManager class comes in handy. This class provides methods
to add, remove, or insert layers from a game, and also provides a single method
to paint all of these layers to the underlying Graphics object. This
means that you don't need to individually call the paint() method
of each of the layers of a game.

An instance of LayerManager is created using its no-args constructor.
Layers are then added, removed, or inserted into it by using the methods

append(Layer 
  layer)
, remove(Layer layer), and
insert(Layer l, int 
  index)
, respectively. The order in which layers are added is important,
because this order determines which layer is painted first, as this becomes
the z-order index. The layer at index 0 is painted on top of all the other
layers, and hence, is closest to the user, and so on.

In our game, the start() method now needs to be modified, as shown here:

// creates the layermanager
manager = new LayerManager();

// and adds layers to it
manager.append(coupleSprite);

// creates the game background
createBackground();

manager.append(background);

As you can see, the coupleSprite layer will be closest to the
user and the background layer will be farthest back, based on their indices.
The buildGameScreen() method now does not need to paint the background
(as the LayerManager will paint the background now), and therefore the
background.paint(g) line needs to be removed from this method.
Finally, in the previous section, you used the coupleSprite to
paint it on the screen instead of the coupleImage. Now even that
is not required, as the LayerManager will do this for you.
Remove coupleSprite.paint(g) from the updateGameScreen()
method and replace it with manager.paint(g, 0, 0). As you can see,
all calls to individual layers' paint() methods have been replaced
with a single call to the LayerManager's paint() method.
The last two parameters represent the location at which the manager should paint.
Since the background and carSprite are responsible
for their own positioning, you can leave these parameters as is (that is, paint
from the device origin).

Listing 3 shows the revised updateGameScreen().
The lines that are to be removed are retained as comments to make it easy to locate the changes.

private void updateGameScreen(Graphics g) {

  // the next two lines clear the background
  g.setColor(0xffffff);
  g.fillRect(0, 0, getWidth(), getHeight());

  // creates the game borders
  buildGameScreen(g);

  // draws the couple image according to current
  // desired positions
  /*g.drawImage(
    coupleImg, coupleX,
    coupleY, Graphics.HCENTER | Graphics.BOTTOM);*/

  // animates the sprite
  coupleSprite.nextFrame();

  // moves the sprite based on its reference pixel
  coupleSprite.setRefPixelPosition(coupleX, coupleY);

  // paints it on the buffer
  // coupleSprite.paint(g);

  // the manager paints all the layers
  manager.paint(g, 0, 0);

  // this call paints off screen buffer to screen
  flushGraphics();

}

Listing 3. Updated updateGameScreen() method







Adding More Sprites and Detecting Collisions

What fun is a single lonely sprite jumping around for no obvious purpose? It's
time to introduce another sprite, in the form a car sprite that
will randomly appear at several locations across the game screen. The jumping/shining
couple will need to bump into these evil car manifestations to defeat them!
The more cars that are hit by the couple in a fixed time, the higher the score.

With the game objectives now clear, let's first create a class that will keep
track of the time so that the game can be stopped once the time has expired.
Listing 4 shows the code for the Clock class.

package com.j2me.part3;

import java.util.TimerTask;

public class Clock extends TimerTask {

  int timeLeft;

  public Clock(int maxTime) {
    timeLeft = maxTime;
  }

  public void run() {
timeLeft--;
  }

  public int getTimeLeft() { return this.timeLeft; }
}

Listing 4. The Clock class that will keep track of the game time

The Clock class extends the TimerTask class, whose
run() method gets executed after a predefined time. Here, it reduces
the maxTime variable every second, which helps us keep
track of the time. To use the Clock class, create and start it
just before the infinite loop inside of the run() method of the MyGameCanvas
class is executed, as shown here:

// before going in the loop, start the timer clock with a
// 30 seconds countdown
clock = new Clock(30);
new Timer().schedule(clock, 0, 1000);

Of course, now the infinite loop must be preempted with a flag that stops
the loop from running when the time has expired. To do this, define a Boolean
flag called stop, as shown here:

// the flag that tells the game to stop
private Boolean stop = false;

Use it in the while loop as while(!stop) and enter the first lines
of code in the verifyGameState() method:

private void verifyGameState() {

  if(clock.getTimeLeft() == 0) {
stop = true;
return;
  }
}

Finally, the user needs to be informed of the time left in the game. To do
this, add a method called showTimeLeft(Graphics g), as shown here:

private void showTimeLeft(Graphics g) {

  // what does the clock say
  int timeLeft = clock.getTimeLeft();

  // if less than 6 seconds left
  // flicker time with red and black
  if(timeLeft < 6) {
if((timeLeft % 2) == 0)
  g.setColor(0xff0000);
else
  g.setColor(0x000000);
  }

  // draw the time left string
  g.drawString("Time Left: " + timeLeft + " seconds", 0, 0, 0);

  // reset the color
  g.setColor(0x000000);

}

This is called at the end of the buildGameScreen() method. Figure
8 shows a snapshot of the game as it looks now.

Figure 8
Figure 8. Game with time left showing

It is time to add a
new (actually several new) sprites into this game. Listing 5 shows the code
for the car sprite in a separate class called CarSprite. This code uses the
image of a car shown in Figure 9.

Figure 9

Figure 9. Image for car sprite

package com.j2me.part3;

import java.util.Random;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.lcdui.game.LayerManager;

public class CarSprite implements Runnable {

  public CarSprite(MyGameCanvas parent) {

this.parent = parent;
this.manager = parent.getManager();

  }

  public void start() {

// first load the car image
try {
  carImage = Image.createImage("/car.gif");
} catch(Exception e) { System.err.println(e); return; }

// next start the thread that will display cars
// are random locations
runner = new Thread(this);
runner.start();
  }

  public void run() {

try {
  while(true) {

    // create a random car
    randomCar();

    // wait before creating another one
    Thread.currentThread()Sleep(500);
  }
} catch(Exception e) { System.err.println(e); }
  }

  // creates and displays a car at a random location
  private void randomCar() {

// if maximum cars are being shown return
if(currentCars == MAX_CARS) return;

// create a new car sprite
carSprite = new Sprite(carImage, 10, 10);

// generate the random places where cars may appear
int randomCarX = parent.getRandom().nextInt(parent.GAME_WIDTH);
int randomCarY =
  (parent.BASE -
    parent.getRandom().nextInt(parent.MAX_HEIGHT + 1) -
    carSprite.getHeight());

// make sure that these places are within bounds
if(randomCarX < parent.GAME_ORIGIN_X) randomCarX = parent.CENTER_X;
if(randomCarY < (parent.BASE - parent.MAX_HEIGHT))
  randomCarY = parent.CENTER_Y;

// set this newly created car sprite in its random position
carSprite.setPosition(randomCarX, randomCarY);

// add it to the manager at index 0
manager.insert(carSprite, 0);

// increase the no of cars created
currentCars++;

  }

  public void checkForCollision() {

// if there are no cars being shown (only background and couple)
if(manager.getSize() == 2) return;

// iterate through the layers, remember don't worry about
// the last two because they are the couple and background
for(int i = 0; i < (manager.getSize() - 2); i++) {

  // if collision occurs
  if(parent.getCoupleSprite().collidesWith(
(Sprite)manager.getLayerAt(i), true)) {

    // remove the offending car
    manager.remove(manager.getLayerAt(i));

    // reduce the no of cars on display
    currentCars--;

    // and increase the no of cars hit
    carsHit++;

  }
}
  }

  // the no of cars hit
  public int getCarsHit() {
return carsHit;
  }

  // the car sprite
  private Sprite carSprite;

  // the car image
  private Image carImage;

  // the no of current cars in display
  private int currentCars;

  // the parent canvas
  private MyGameCanvas parent;

  // the parent canvas's layer manager
  private LayerManager manager;

  // runner
  private Thread runner;

  // tracks the no of cars hit
  private int carsHit;

  // the maximum no of cars to create
  private static final int MAX_CARS = 20;

}



Listing 5. Code to create several car sprites

The CarSprite class implements Runnable, as
it needs to spawn several new car sprites every half a second. The run() method calls the randomCar() method
after sleeping for 500 milliseconds. The randomCar() method checks
if the number of existing car sprites hasn't exceeded the limit, then creates
a new sprite using the car image loaded earlier. It then calculates a random
position for this sprite to appear at, making sure that this random position is
within the game bounds. It sets this newly created sprite in this random position
and adds the sprite to the LayerManager at index 0, so that it
becomes the most recent (and closest to the user) sprite.

This class also provides a method to check for collision of the couple with
the random cars. The checkForCollision() method iterates through
the current car sprites being shown by the LayerManager, and uses
the collidesWith() method of the Sprite class to check
for collision. This method returns a Boolean true when collision has occurred,
and accepts a layer, an image, or another with which sprite to check collision. It
also accepts a flag to indicate if collision detection should take into account
the transparent pixels around an image, or only opaque pixels. When a collision
is detected, the number of cars hit is incremented and the number of cars visible
is decremented.

To use the CarSprite class, append the following lines of code
at the end of the start() method of the MyGameCanvas
class.

// create the car sprite thread and start it
carSprite = new CarSprite(this);
carSprite.start();

Also add the following line of code at the end of the verifyGameState()
method.

carSprite.checkForCollision();

Thus, the CarSprite thread starts spawning new cars, up to a maximum
number of cars. Once the user hits a car by moving the jumping/shining couple with
an unpredictable bounce, the car disappears. This is checked in the verifyGameState()
method by calling the checkForCollision() method on the CarSprite
thread. More cars keep appearing till the time runs out. Figure 10 shows a typical
game in progress.

Figure 10
Figure 10. A typical game in progress after adding the car sprites

All that is left now is to inform the user about the number of cars that he has
hit. After the while() loop has exited, add a call to a new method
called showGameScore(getGraphics()), and add this new method as
shown here:

// at the end of the game show the score
  private void showGameScore(Graphics g) {

  // create a base rectangle
  g.setColor(0xffffff);
  g.fillRect(0, CENTER_Y - 20, getWidth(), 40);

  g.setColor(0x000000);

  // and show the score
  g.drawString("You hit " +
    carSprite.getCarsHit() + " cars.",
    CENTER_X, CENTER_Y,
    Graphics.HCENTER | Graphics.BASELINE);

  flushGraphics();
}

This draws a small rectangle in the middle of the screen at the end of the
game showing the number of cars hit by the player. A typical game ending is
shown in Figure 11.

Figure 11
Figure 11. A typical game ending and the message displayed

You can, of course, display this information in any format or location that
you want.

Conclusion

This part of the J2ME tutorial series was a long-winded but comprehensive look
at the gaming API of MIDP 2.0. You learned how to use the classes of this API
using a full-fledged example and built a game successfully. You also learned
the basics of game building through this example.

In the next few parts of this tutorial, you will learn how to add multimedia
to your MIDlets, something that can be very useful in J2ME games. You will
also learn how to use the record-store management API to consistently store
information permanently. In the meantime, you can download and experiment with the
game that you built in this article by downloading
the full source code.
You can also play with the final game by downloading the JAD
and JAR files.

width="1" height="1" border="0" alt=" " />
Vikram Goyal is the author of Pro Java ME MMAPI.

Comments

Can you upload your code

Can you upload your code again? Thanks you!