Professional Documents
Culture Documents
net/print/219592
javax.microedition.lcdui.game,
1 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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 GameCanvas class is made useful by extending the functionality of the Canvas
class (from the previous article [11] ). 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.
2 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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.
// main class
public MainClass {
private GameCanvas canvas = new MyGameCanvas();
public MainClass() {
// start a thread that will run infinitely
canvas.start();
}
public MyGameCanvas() {
// instantiation code
}
// or while(keeprunning = true)
// where keeprunning is an instance variable
while(true) {
3 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
checkUserInput();
updateGameScreen(getGraphics());
Thread.sleep(milliseconds);
}
}
}
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 [11] class: DOWN_PRESSED, UP_PRESSED, RIGHT_PRESSED,
LEFT_PRESSED, FIRE_PRESSED, GAME_A_PRESSED, GAME_B_PRESSED, GAME_C_PRESSED, and GAME_D_PRESSED.
4 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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 [13] 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 MyGameCanvas() {
super(true);
}
try {
5 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
checkUserInput();
// update screen
updateGameScreen(getGraphics());
6 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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;
MyGameCanvas gCanvas;
public GameMIDlet() {
gCanvas = new MyGameCanvas();
}
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
7 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
will need this image file: , named couple.gif [14] , in the res folder of your
project, or you can use a similar-sized image. Figure 1 shows the expected
output.
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 infinite loop in the run() method follows the game structure that I
described in the sidebar earlier. This loop sleeps for 30 milliseconds before
8 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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.
(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.
9 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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:
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,
10 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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:
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:
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:
11 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
} else {
// the couple are on their way down, have they reached base?
if(coupleY < BASE) {
// but make sure that this it is atleast greater than the image height
if(hyper > coupleImg.getHeight()) jumpHeight = hyper;
}
}
}
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
12 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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.
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.
13 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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.
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:
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
14 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
15 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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.
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:
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
16 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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.
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.
In our game, the start() method now needs to be modified, as shown here:
// creates the layermanager
manager = new LayerManager();
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
17 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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.
18 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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;
int 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:
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:
Use it in the while loop as while(!stop) and enter the first lines of code in the
19 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
verifyGameState() method:
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:
20 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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.
import java.util.Random;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.lcdui.game.LayerManager;
this.parent = parent;
this.manager = parent.getManager();
21 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
runner.start();
}
try {
while(true) {
22 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
// 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)) {
}
}
}
// runner
private Thread runner;
23 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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.
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.
24 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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:
g.setColor(0x000000);
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.
25 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
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
[12] . You can also play with the final game by downloading the JAD [15] and JAR
[16] files.
Links:
[1] http://www.java.net/author/vikram-goyal
26 of 27 10/03/2010 01:14 AM
J2ME Tutorial, Part 3: Exploring the Game API of... http://www.java.net/print/219592
[2] http://www.java.net/article/2005/06/20/j2me-tutorial-part-3-exploring-game-api-
midp-20#overview
[3] http://www.java.net/article/2005/06/20/j2me-tutorial-part-3-exploring-game-api-
midp-20#primer
[4] http://www.java.net/pub/a/today/2005/07/07/j2me3.html?page=2#gamecanvas
[5] http://www.java.net/pub/a/today/2005/07/07/j2me3.html?page=3#characteristics
[6] http://www.java.net/pub/a/today/2005/07/07/j2me3.html?page=4#backgrounds
[7] http://www.java.net/pub/a/today/2005/07/07/j2me3.html?page=5#sprites
[8] http://www.java.net/pub/a/today/2005/07/07/j2me3.html?page=5#layermanager
[9] http://www.java.net/pub/a/today/2005/07/07/j2me3.html?page=6#adding_sprites
[10] http://www.java.net/pub/a/today/2005/07/07/j2me3.html?page=6#conclusion
[11] http://www.java.net/pub/a/today/2005/05/03/midletUI.html
[12] http://www.java.net/%3Ccs_comment
[13] http://www.java.net/pub/a/today/2005/02/09/j2me1.html
[14] http://www.java.net/images/2005/07/couple.gif
[15] http://www.java.net/today/2005/07/07/part3_game_examples.jad
[16] http://www.java.net/today/2005/07/07/part3_game_examples.jar
27 of 27 10/03/2010 01:14 AM