Skip to main content

Writing Cool Games for Cellular Devices

April 25, 2006

{cs.r.title}







One of the main usages of J2ME is game programming. In this
article, I'll explain this by way of a standalone game that I
developed. This application is a simple basketball game, in which
the player plays against a computer opponent. Each game lasts
exactly two minutes. The aim of the game is to shoot as many baskets
as you can and to prevent the computer opponent from doing the
same.

Main Functions

A typical game in J2ME environment might consist of the
following:

  • A welcome screen that redirects automatically to the main
    menu.
  • The main menu.
  • The main screen. This is where the game is actually
    played.
  • Level menu (set the game difficulty, etc.).
  • Instruction screen.
  • About screen.

The main screen and the welcome screen are executed by classes
that are extends from the "http://today.java.net/pub/a/today/2005/05/03/midletUI.html?page=last">
Canvas
. This class is defined as being in the
low-level API, meaning that it allows full control of the
display at the pixel level. All the other screens are executed by
high-level API classes that use J2ME standard controls.

When we program apps for J2ME, we must also consider how to
handle unique system events such as call interrupts. When we deal
with this event, we might want to freeze the current game state or
even be able to save this game state after we exit the
application.

In order to get started, we'll have to download the "http://java.sun.com/products/sjwtoolkit/download-2_2.html">J2ME
wireless toolkit
. This is the most elementary SDK for this
environment. The main objective of this toolkit is to compile the
Java source files and to produce two deployment files: a JAR file
that encapsulate all of the class files and a JAD (Java Application
Descriptor) file. The JAD file provides data about the application
such as vendor, JAR size, version number, etc. This SDK can be
integrated with more advanced IDEs such as "http://www.eclipse.org/">Eclipse or "http://www.netbeans.org/">NetBeans. You can learn more about
getting started with the wireless toolkit in the article " "http://developers.sun.com/techtopics/mobility/midp/articles/wtoolkit/">
Wireless Development Tutorial Part I
."

Game Structure

Figure 1 shows the class structure of the game.

General structure of the game
Figure 1. General structure of the game

The game starts with an opening screen, and after five seconds
redirects to a menu screen. From the menu screen, the player
has the option to start the game, set the level, display an "about"
screen, or display the instructions. Each screen is managed by its
own class. For example, the welcome screen is managed by the
Splash class, the main game screen is managed by
MainScreen, and so forth.

The Midlet Class

A very important class that is not displayed in the diagram
above is the TestMidletMIDlet class. This class
extends from the Midlet class, and its job is to
initialize all of the other classes and controls. One major method of
the Midlet class is the startApp()
method. Normally, when a midlet is called, it is
initially in a paused state. If everything runs normally and
no exception occurs, then the midlet enters the active
state. This is done by calling the startApp() method.
Here is an example:


public void startApp() {
        // some code here...
        display.setCurrent(splash);
}
                

For this stage, the most important call is
display.setCurrent(splash). This method call sets the
midlet's Displayable; in other words, it determines which of
our screens is to be displayed on the device. In this example it
will be the Splash screen, as seen in Figure 2.

Splash screen
Figure 2. Splash screen

Splash Screen

The splash screen is the first screen that appears when the
application starts up. It is extends the "http://today.java.net/pub/a/today/2005/05/03/midletUI.html?page=last">
Canvas
class. The Canvas class, as the
so-called low-level API, allows full control of the display at the
pixel level. In order to paint something, we have to override the
paint() method. For example:


public void paint(Graphics g) {
        // sets background color
        g.setColor(255, 255, 255);
        // fill the screen with the color
        //defined previously
        g.fillRect(0, 0, this.getWidth(),
                this.getHeight());
        // draw some image
        g.drawImage(screenshot,
                (this.getWidth() - 128) / 2,
                (this.getHeight() - 128) / 2,
                Graphics.TOP|Graphics.LEFT);
}

                

After a period of five seconds, or by pressing any key, our game
changes the display to the main menu screen. In order to handle key
presses we have to implement the "http://developers.sun.com/techtopics/mobility/midp/articles/event/index.html">
CommandListener
interface. All key press
events will be handled by the function
commandAction().


public void commandAction(Command command,
        Displayable displayable) {
        // stops timer operation
        timer.cancel();
        /*
        switch the display to menu. parent
        is the Midlet object which actually does the
        job.
        */
        parent.setCurrent("MainMenu2");
}
                

"http://java.sun.com/developer/J2METechTips/2001/tt0416.html">

Timer
and TimerTask are two classes that
allow us to execute a function after a period of predefined time.
We use these classes to automatically switch the display to the
next screen.


public void startTimer() {
        // TimerTask defines the task or subroutine
        // to be executed.
        TimerTask gotoMenu = new TimerTask()  {
                public void run() {
                        timer.cancel();
                        parent.setCurrent("MainMenu2");
                }
        };
        // Timer class creates and manages threads on
        // which the tasks are executed
        timer = new Timer();
        timer.schedule(gotoMenu, 5000);
}
                

The MainMenu Class

The MainMenu class, shown in Figure 3, shows the
main menu of the game.

Main Menu Screen
Figure 3. Main menu screen

The main menu has five elements:

  • Continue: Continues a game that has been previously
    stopped.
  • Start Game: Starts a new game.
  • Choose Level: Choose the speed of the computer player.
  • Instructions: Shows an instructions screen.
  • About: Shows an about screen.

In J2ME, the class "http://java.sun.com/developer/J2METechTips/2001/tt0622.html">

List
is responsible for displaying lists or menus, which
is why MainMenu subclasses it. As in the
Splash class, we have to implement the "http://developers.sun.com/techtopics/mobility/midp/articles/event/index.html">
CommandListener
interface in order to handle key
presses.

The MainScreen Class

MainScreen, shown in Figure 4, is the class that displays
the actual basketball game being played. The
MainScreen class is extended from the
Canvas class.

Figure 4
Figure 4. Main game screen







Rules of the Game

As mentioned before, this is a standalone game in which the
player plays against the computer. There are two players on the
screen. The human player controls the blue player, who tries to put
the ball in the basket on the right. The computer controls the red
player. Each game lasts two minutes, and the player who shoots the
most baskets wins. The human player controls the red player by the
arrow keys or by the number keys: 2 for up, 8 for down, 6 for right, 4 for left, and 5 to shoot the ball.

Elements in the Main Screen

Several graphic elements compose this screen. These include the
background, the players, the ball, etc. Each element must be a PNG
graphic file; these are illustrated in Figures 5 through 8.

Screen background
Figure 5. Screen background

Human player icon
Figure 6. Human player icon

Computer player icon
Figure 7. Computer player icon

Ball icon
Figure 8. Ball icon

When the class MainScreen initializes, we must get
each graphic element from the JAR file and store it in
an Image field. This can be done in the
constructor.


private Image screenShot;

public MainScreen(TestMidletMIDlet parent) {
        // some code...

        try {
                screenShot = Image.createImage
                        ("/Images/screenShot.png");
        }
        catch(IOException e) {}
        // we do this for all the other images
}
                

Handling Key Presses

As in previous classes, we again implement the
CommandListener interface. Here, we only use
CommandListener to pause the game and return to the main
menu. In order to do this, we have to initialize a "http://www-128.ibm.com/developerworks/wireless/library/wi-j2me/">

Command
object first. In our example we label this
object as Pause. The label can be seen in Figure 4 on the bottom
left side of the screen.


// this is the Command object, labeled as "Pause"
private Command pauseCommand =
        new Command("Pause", Command.STOP, 1);
                

All of the CommandListener events are handled by
commandAction().


/**
* Handles command actions
*/
public void commandAction
        (Command c, Displayable d) {
        // verified that the pause button was pressed
        if (c == pauseCommand) {
                // do something
        }
}
                

In the Canvas class, we can also handle key presses
with the keyPressed(int keyCode) and
keyReleased(int keyCode) methods. Every key press
invokes keyPressed(), and every key release invokes
keyReleased(). In our game, we use these functions to
handle all of the arrow and select keys. These are the buttons that
move the human's player icon.


private keyStatus = 0;

/**
* Called when a key is pressed.
*/
protected  void keyPressed(int keyCode) {
        if (this.getGameAction(keyCode) == UP) {
                keyStatus = KEY_NUM2;
        }
        else if(this.getGameAction(keyCode) == DOWN){
                keyStatus = KEY_NUM8;
        }
        else if(this.getGameAction(keyCode) == LEFT){
                keyStatus = KEY_NUM4;
        }
        else if(this.getGameAction(keyCode) == RIGHT){
                keyStatus = KEY_NUM6;
        }
        else if(this.getGameAction(keyCode) == FIRE){
                keyStatus = KEY_NUM5;
        }
        else {
                keyStatus = keyCode;
        }
}

/**
* Called when a key is released.
*/
protected  void keyReleased(int keyCode) {
        keyStatus = 0;
}
                

In general, this method updates the value of the field
keyStatus. If no key is being pressed at the moment,
then the value of keyStatus is 0; otherwise, it is some
integer value. The actual movement of the player will be explained
later.







Algorithm of the Game

This class initializes an internal TimerTask class.
The actual initialization is done by a Timer, which
invokes the TimerTask periodically (by default, every
50 milliseconds, but this is adjustable by the human player on the Level
screen). The TimerTask class executes the function
myMove2().


private Timer timer;

public void startTimer()
{
        // this class is being executed periodically.
        TimerTask mover = new TimerTask()  {
                public void run() {
                        myMove2();
                }
        };

        timer = new Timer();
        // invokes the mover class.
        try {
                // if anything is being set by the level
                // screen
                timer.schedule(mover, parent.getLevel(),
                        parent.getLevel());
        }
        catch (IllegalArgumentException e) {
                timer.schedule(mover, 50, 50);
        }
        
}
                

The myMove2() method has two roles:

  1. It checks the value of keyStatus (which is set by the
    keyPressed() and keyReleased() methods)
    and moves the human player icon accordingly.
  2. it moves the computer player icon according to the situation
    evolving in the game.

As previously explained, each key press (on the arrow and select
keys) sets the value of the keyStatus field. According
to that value, we calculate the coordinates of the human player
icon.


// coordinates of human player icon
private int meX, meY;
// coordinates of the ball
private int xBall, freeBallY, yBall;
// coordinates of the field's corner
private int x1, x2, x3, x4, y1, y2, y3, y4;


private void myMove2()
{
        // some code

        // define me Coordinates
        switch(keyStatus) {
      // up
        case Canvas.KEY_NUM2:
                meY--;
                if (meY < y1)
                        meY = y1;
                        break;
        // down
        case Canvas.KEY_NUM8:
                meY++;
                if (meY > y4)
                        meY = y4;
                break;
        // right
        case Canvas.KEY_NUM6:
                meX++;
                if ((x2 + x3) / 2 < meX) {
                        meX = (x2 + x3) / 2;
                }
                if (ballOwner == 0 && compMode != 5)
                        xBall = meX + 8;
                break;
            // left
        case Canvas.KEY_NUM4:
                meX--;
                if ((x1 + x4) / 2 > meX) {
                        meX = (x1 + x4) / 2;
                }
                if (ballOwner == 0 && compMode != 5)
                        xBall = meX + 8;
                break;
        // fire
        case Canvas.KEY_NUM5:
                if (compMode == 2) {
                        originalY = freeBallY;
                        compMode = 5;
                }
                break;
        default:
        /////
        }

        // some code...
}
                

As the game runs, the computer controls the opposing player's
icon. The CPU player's actions vary according to the situations
that evolve during the game--in different situations, the
computer behaves differently. The int field
compMode stores a "situation code" for every given
moment. The situations are:

  1. Jump ball: This situation occurs only at the beginning of the
    game. Both players' icons are at the middle of the field, and the
    ball icon is right in between them, falling down. We switch
    situation status when either player icon catches the ball.
  2. The human player icon has the ball: When the human player
    moves, we can see the ball being dribbled with the human player.
    The computer moves its player toward the human player, and tries to
    steal the ball when it's close enough.
  3. The computer player icon has the ball: Here, the computer player
    dribbles the ball. In this situation, the computer tries to move
    towards the human player's basket. If the human player icon is in
    front of him, it will try to bypass him. When the computer player
    is near enough to the human's basket, it will try to shoot a
    basket.
  4. Not in use.
  5. The human player shoots the ball: The computer switches to this
    situation when the player presses the select (or fire, on some
    devices) key. We can see the ball being thrown to the basket. If
    the shot is successful, then the two players are replaced on either
    side of the field and the computer switches the situation to 3
    (compMode=3).
  6. The computer player shoots the ball: When the computer player has
    the ball and the computer player icon is near enough to the human
    player's basket, it will automatically try to shoot a basket. If the
    shot is successful, the two players are replaced on either side of
    the field and the computer switches the situation to 2
    (compMode=2).

// coordinates of human player icon
private int meX, meY;
// coordinates of the ball
private int xBall, freeBallY, yBall;
// coordinates of the field's corner
private int x1, x2, x3, x4, y1, y2, y3, y4;
// computer player situation state
private int compMode;


private void myMove2()
{
        // some code...

        // switch by situation
        switch (compMode) {
        /*
        *free ball jumps
        */
        case 1:
                // this method controls the jump
                // movement of the ball
                ballJump();

                // decides who gets the ball


                // computer gets the ball
                if (Math.abs(xBall - compX) <= 21 &&
                        Math.abs(freeBallY - compY) <= 5) {
                        case3Mode = 1;
                        compMode = 3;
                        delay = 0;
                }
                // me kidnaps the ball
                if (Math.abs(meX - xBall) <= 21 &&
                        Math.abs(meY - freeBallY) <= 5) {
                        compMode = 2;
                        delay = 0;
                        delay2 = 0;
                }

                // calculate comp moves
                if (compY > freeBallY) {
                        compY--;
                }
                if (compY < freeBallY) {
                        compY++;
                }
                if (compX > xBall) {
                        compX--;
                }
                if (compX < xBall) {
                        compX++;
            }
                compCheckBorders();
                break;
        /*
        *the ball is at me player
        */
        case 2:
                xBall = meX + 6;
                freeBallY = meY;
                ballOwner = 0;
                delay2++;
                ballJump();
                // computer steals the ball
                if (Math.abs(meX - compX) <= 21 &&
                        Math.abs(meY - compY) <= 5 &&
                                delay >= 10)         {
                        case3Mode = 1;
                        compMode = 3;
                        delay = 0;
                        delay2 = 0;
                }
                if (delay2 > 30) {
                        if (Math.abs(meX + 20 - compX) >
                                Math.abs(meY - compY)) {
                                if (compX > meX + 20)
                                        compX--;
                                else
                                        compX++;
                        }
                        else {
                                if (compY > meY)
                                        compY--;
                                else
                                        compY++;
                        }
                        // check borders for comp players
                        compCheckBorders();
                }
                break;
        /*
        *the ball is at computer player
        */
        case 3:
                xBall = compX - 3;
                freeBallY = compY;
                ballOwner = 1;
                ballJump();
                // me kidnaps the ball
                if (Math.abs(meX - compX) <= 21 &&
                        Math.abs(meY - compY) <= 5 &&
                                delay >= 5) {
                        compMode = 2;
                        delay = 0;
                        delay2 = 0;
                }
                /*
                here we compute how the computer
                player icon will move
                */
                switch (case3Mode)
                {
                // go back from player
                case 1:
                        compX++;
                        if (compX > meX + 29) {
                                case3Mode = 2;
                        }
                        break;
                // go side from player
                case 2:
                        if (compY < myHeight * 3 / 4) {
                                compY++;
                                compYDirection = 1;
                                case3Mode = 3;
                        }
                        compY--;
                        compYDirection = 0;
                        case3Mode = 3;
                        break;
                // continue go side
                case 3:
                        if (compYDirection == 1 &&
                                compY <= meY + 15) {
                                compY++;
                                if (compY > y4)
                                        compY = y4;
                        }
                        else if (compYDirection == 0 &&
                                compY >= meY - 15) {
                                compY--;
                                if (compY < y1)
                                        compY = y1;
                        }
                        else {
                                case3Mode = 4;
                        }
                        break;
                // go forward
                case 4:
                        if (compX > myWidth * 11 / 32) {
                                compX--;
                        }
                        else if (compX < myWidth * 1 / 4 - 3)
                        {
                                compX++;
                        }
                        else {
                                originalY = freeBallY;
                                compMode = 6;
                        }
                break;
                        // finally throw the ball
                case 5:
                        case3Mode = 1;
                        originalY = freeBallY;
                        compMode = 6;
                        yBall = 9;
                        deltaX = 1;
                        break;
                default:
                        //
                }

                if (compX - meX <= 15 &&
                        Math.abs(compY - meY) <= 10) {
                        case3Mode = 1;
                }
                // check borders for comp player
                compCheckBorders();
                break;
        /*
        *not in use
        */
        case 4:
                break;
        /*
        *me throws the ball
        */
        case 5:
                freeBallY = originalY - deltaY[deltaX];
                deltaX++;
                xBall++;

                // checks if the ball hits the basket
                if (deltaX >= 29 &&
                        (xBall >= (x2 + x3) / 2 - 10 &&
                                xBall <= (x2 + x3) / 2 + 10)) {
                        myScore += 2;
                        oldY = 0;
                        compMode = 3;

                        // reset player and the balls
                        meX = myWidth * 3 / 16;
                        meY = myHeight * 3 / 4;
                        compX = myWidth * 13 / 16;
                        compY = myHeight * 3 / 4;
                        xBall = compX - 8;
                        yBall = 3;
                        deltaX = 0;
                }
            // if the ball reaches the floor
                else if (deltaX >= 29 &&
                        (xBall < (x2 + x3) / 2 - 10 ||
                                xBall > (x2 + x3) / 2 + 10)) {
                        freeBallY = meY;
                        compMode = 1;
                        oldY = 0;
                        deltaX = 0;
                }
                        else {
                        oldY = freeBallY;
                        }
                break;
        /*
        *comp throws the ball
        */
        case 6:
                freeBallY = originalY - deltaY[deltaX];
                deltaX++;
                xBall--;

                // checkes if the ball hits the basket
                if (deltaX >= 29) {
                        compScore += 2;
                        oldY = 0;
                        compMode = 2;

                        // reset player and the balls
                        meX = myWidth * 3 / 16;
                        meY = myHeight * 3 / 4;
                        compX = myWidth * 13 / 16;
                        compY = myHeight * 3 / 4;
                        xBall = meX + 8;
                        yBall=  3;
                        deltaX = 0;
                }
                else {
                        oldY = freeBallY;
                        }
                break;
        default:
                //sdfgsdfgsdgf
        }

        /*
        after the coordinates of the human player
        icon, the computer player icon and the ball
        has been set we can go to the last stage,
        which is painting the screen.
        */
        repaint();
}

/*
this method controls the jump movement of the
ball
*/
private void ballJump()
{
        if (ballDir == 0) {
                yBall--;
                if (yBall < 3) {
                        ballDir = 1;
                }
        }
        else {
                yBall++;
                if (yBall > 9) {
                        ballDir = 0;
                }
        }
}

/*
this function checks if the computer
passed the border of the field
*/
private void compCheckBorders()
    {
        if (compY < y1) {
            compY = y1;
        }
        if (compY > y4) {
            compY = y4;
        }
        if (compX > (x2 + x3) / 2) {
            compX = (x2 + x3) / 2;
        }
        if (compX < (x1 + x4) / 2) {
            compX = (x1 + x4) / 2;
        }
    }
                

After all of the coordinates have been set, we can proceed to the
final stage, which is to actually paint the screen. This is done by
calling the paint() method. We call this at the end of
the move() function, by calling
repaint().


/**
* paints the screen
*/
public void paint(Graphics g) {

        Graphics saved = g;
        // these fields show the clock in the game
        String clockMinuteStr = new String();
        String clockSecondStr = new String();

        // initialize a buffered image
        if (offscreen != null) {
                g = offscreen.getGraphics();
        }

        // cleans the screen
        g.setColor(255, 255, 255);
        g.fillRect(0, 0, this.getWidth(),
                this.getHeight());

        // define corners of field
        x1 = myWidth*3/16;
        x2 = myWidth*13/16;
        x3 = myWidth - 2;
        x4 = 2;
        y1 = myHeight * 1 / 2;
        y4 = myHeight - 1;

        // draw solid background
        g.setColor(255, 255, 255);
        g.fillRect(offsetWidth, offsetHeight,
                myWidth, myHeight);
        g.setColor(0, 0, 0);
        g.drawImage(screenShot, offsetWidth,
                offsetHeight, 0);

        // draw Scores
        g.fillRect(offsetWidth + myWidth / 4,
                offsetHeight + myHeight / 8,
                myWidth /  2, myHeight / 4);
        g.setColor(255, 255, 255);
        g.drawRect(offsetWidth + myWidth / 4,
                offsetHeight + myHeight / 8,
                myWidth /  2, myHeight / 4);
        clockMinuteStr =
                String.valueOf(clockMinute);
        clockSecondStr =
                String.valueOf(clockSecond);
        if (clockMinuteStr.length() == 1)
                clockMinuteStr = "0" + clockMinuteStr;
        if (clockSecondStr.length() == 1)
                clockSecondStr = "0" + clockSecondStr;
        g.drawString(":", offsetWidth + myWidth / 2,
                offsetHeight + 15,
                Graphics.TOP|Graphics.LEFT);
        g.drawString(clockMinuteStr,
                offsetWidth + myWidth / 2 - 15,
                offsetHeight + 17,
                Graphics.TOP|Graphics.LEFT);
        g.drawString(clockSecondStr,
                offsetWidth + myWidth / 2 + 5,
                offsetHeight + 17,
                Graphics.TOP|Graphics.LEFT);
        g.drawString(String.valueOf(myScore),
                offsetWidth + myWidth / 2 - 25,
                offsetHeight + 32,
                Graphics.TOP|Graphics.LEFT);
        g.drawString(String.valueOf(compScore),
                offsetWidth + myWidth / 2 + 20,
                offsetHeight + 32,
                Graphics.TOP|Graphics.LEFT);

        // paint player me
        g.drawImage(mePlayer, offsetWidth + meX,
                offsetHeight + meY - 19, 0);

        // paint Computer Player
        g.drawImage(compPlayer, offsetWidth + compX,
                offsetHeight + compY - 19, 0);

        //paintBall
        g.drawImage(tinyBall, offsetWidth + xBall,
                offsetHeight + freeBallY - yBall, 0);


        // paints the buffered image
        if (g != saved) {
                saved.drawImage(offscreen, 0, 0,
                Graphics.LEFT | Graphics.TOP);
        }

}
                







Handling Call Interrupts

When an incoming call occurs in the middle of the game, the
Canvas screen might disappear, so we might want to
freeze the game state (save all of the data regarding the players'
positions, ball position, number of point, time left, etc.). In our game, the freezing of the game is done by stopping the
timer. There are two functions related to the canvas disappearing
and reappearing. hideNotify() is called after the
Canvas disappears and showNotify() is
called when the Canvas reappears. We stop the timer in
the hideNotify() event and we reactivate the timer in the
showNotify() event.


/**
* called when the screen disappears
*/
protected void hideNotify() {
        if (finishGame == 0) {
                parent.setCurrent("MainMenu");
        }
        // stops the internal timer and thus
        // freezes the game.
        stopTimer();
}

/**
* called when the screen reappears
*/
protected void showNotify() {
        // restarts the internal timers.
        startTimer();
}
                

Saving Persistent Data

J2ME applications have a method to store data even after the
user has terminated the application. We manage this data with the
"http://www-128.ibm.com/developerworks/wireless/library/wi-rms/">

RecordStore
class. In our application, we need to store
persistent data in order to enable the player to stop the game at
any given moment, exit the application, and return to the game some
time later and to continue exactly from the moment it stopped. The
data that we need to store includes: time left for game,
coordinates of the two player icons, ball coordinates, etc. We
arrange all of this data in a byte[] array, and only after
that we can store it.


/**
* Writes all the game data into recordstore
* @param rec
*/
public void writeRMS(byte[] rec) {
        try {
                rs = RecordStore.openRecordStore("pocket",
                        true);
                if (rs.getNumRecords() > 0)
                        rs.setRecord(1, rec, 0, 31);
                else
                        rs.addRecord(rec, 0, 31);
                rs.closeRecordStore();
        }
        catch (Exception e) {}
}

/**
* Reads the data from the recordstore
* @return
*/
public byte[] readRMS() {
        byte[] rec = new byte[31];
        try {
                rs = RecordStore.openRecordStore("pocket",
                        true);
                rec = rs.getRecord(1);
                rs.closeRecordStore();
        }
        catch (Exception e) {}
        return rec;
}

/**
*
* Deletes all the record stores
*/
public void deleteRMS() {
        if (RecordStore.listRecordStores() != null) {
                try {
                        RecordStore.deleteRecordStore
                                ("pocket");
                }
                catch (Exception e) {}
        }
}
                

Other Screens

Two other screen in our application are
InstructionsForm (shown in Figure 9) and
AboutForm. We extend these two classes from the
Form class and also implement
CommandListener in order to handle key presses.
Form class is part of the "high-level" API, and allows
us to easily insert plain text, images, and other items to be
displayed.

Figure 9
Figure 9. Instruction screen


public class InstructionsForm extends Form
        implements CommandListener {
        private TestMidletMIDlet parent;
        private Command mainMenu =
                new Command("Back", Command.BACK, 1);

        public InstructionsForm(TestMidletMIDlet
                parent) {
                super("Instructions");
                addCommand(mainMenu);
                setCommandListener(this);

                // insert some text to be seen.
                this.append("The objective of this game
                        is to shoot as many baskets as
                        possible while preventing your
                        opponent shoot to your basket.\n\n");
                this.append("move your player using
                        4 for moving left, 2 for moving up,
                        6 for moving right and 8 for moving
                        down.\n\n");
                this.append("press 5 to throw the ball");

                this.parent = parent;
        }

        public void commandAction(Command c,
                Displayable d) {
                if (c == mainMenu) {
                        parent.setCurrent("MainMenu2");
                }
        }
}
                

Conclusion

In this article, we have discussed some of the most common
features in J2ME environment. These features include the
MIDlet class, which is the base class for all J2ME
applications, the low-level API's Canvas class, and
high-level API classes such as List and
Form. We also covered the organizational structure of
the game, such as the typical screens of the application.

As I mentioned before, this is a brief description of a typical
J2ME game. Although games are abundant in the handheld environment,
there are many other uses for ME applications, such as stock quote
readers, RSS readers, etc.

Resources

width="1" height="1" border="0" alt=" " />
Kobi Krasnoff is a serious programmer and computer hobbyist.
Related Topics >> Mobility   |   Programming   |