Slater, S. (2007) JAVA based MIDP 2.0 tutorials: Tutorial 4

advertisement
Complementary Mobility
Building an interactive application Midlet with MIDP
Table of Contents
Introduction .............................................................................................................................................. 2
First steps .................................................................................................................................................. 2
Preparing for an interactive application Midlet........................................................................................ 3
Application Design Considerations ........................................................................................................... 4
Application Code ....................................................................................................................................... 7
Analysing the program code ................................................................................................................... 16
Testing the interactive application ......................................................................................................... 18
4: Building an interactive application Midlet with MIDP
Page: 1
Complementary Mobility
Building an interactive application Midlet with MIDP
Introduction
The purpose of this document is to explain how to create a basic interactive application Midlet using
Java and the MIDP/CLDC library. Java MIDP applications are typically called Midlets and an introduction
to MIDP and installing the MIDP development environment can be found in part one of this
Complementary Mobility series. This article assumes you are using Windows XP as your host PC
operating system.
Articles in this series include:





1: Introduction to Mobile Application Development
2: Beginning application development with MIDP
3: Transferring an application Midlet to a mobile device
4: Building an interactive application Midlet with MIDP
5: Building a multimedia application with MIDP
First steps
As described in a previous chapter you must ensure you have a suitable development environment and
have installed the Java wireless toolkit.
The development of an interactive uses Midlet uses startApp(), pauseApp() and destroyApp() methods
as shown in the following diagram.
4: Building an interactive application Midlet with MIDP
Page: 2
Complementary Mobility
Building an interactive application Midlet with MIDP
Preparing for an interactive application Midlet
The first step is to create a new Project; our project will expand the myApp application from the
previous articles so some of the code for this project is the same as the previous article but with a few
additions/modifications for our application to be interactive. Our new Project will be called myApp2 so
click on the New Project button in the toolkit then type in myApp2 for both the Project Name and Class
Name then click Create Project.
The folder structure will be created and you will be informed of progress in the toolkit output window.
4: Building an interactive application Midlet with MIDP
Page: 3
Complementary Mobility
Building an interactive application Midlet with MIDP
Application Design Considerations
To demonstrate the interactive nature of Midlets this application uses the previously developed
framework and builds upon it to produce a simple block game. The idea of the game is to clear all the
blocks by matching groups of 3 or more blocks of the same colour either horizontally or vertically. The
player is assigned a random block at the top of the game area and they can move left or right then press
down to release the block. If the block cannot be released because the column is full then the game is
over. When a block is released another random block will be allocated to the player. Although the game
works and is playable it is very rudimentary and far from being a completed product as its primary
purpose is to demonstrate interactive components.
Some images will be needed for this application and for debugging purposes they should be placed into
the same folder as the compiled Class files. The images are just square blocks filled with colour – two
versions exist – one with 9x9 pixels and the other with 20x20 pixels. The purpose for this is to cater for
both small screen devices and larger screen devices.
One of the first things the application should do is determine the screen size of the mobile device. This
can be achieved using the methods getWidth() and getHeight(). The screen draws from the top left
which is location (0,0). The co-ordinates are given using an (x,y) system where x goes across the screen
horizontally and y is drawn vertically. The way the program works is to first create a portion of memory
to store an image based on the width and height of the current device. This will be an off-screen image
buffer where all the drawing will take place. At regular intervals this off-screen image will be transferred
to the display on the device. This program also makes use of the Java Random() function. This produceds
a pseudo random number in a chosen range and is called using the nextInt() method.
The game uses a number of states to control its game loop. State 0 is the initial state which is the menu,
when the game is played the state is changed to 1. If the player clears the grid the game state changes
to 98 to show the congratulatory message. The final state 99 exits the run() loop. An additional game
state 97 is included which offers a game over message in case no blocks can be placed.
The game takes place on an 8x8 grid, numbered as follows:
(0,0)
(1,0)
(2,0)
(3,0)
(4,0)
(5,0)
(6,0)
(7,0)
(0,1)
(1,1)
(2,2)
(3,3)
(4,1)
(5,1)
(6,1)
(7,1)
(0,2)
(1,2)
(2,2)
(3,2)
(4,2)
(5,2)
(6,2)
(7,2)
(0,3)
(1,3)
(2,3)
(3,3)
(4,3)
(5,3)
(6,3)
(7,3)
(0,4)
(1,4)
(2,4)
(3,4)
(4,4)
(5,4)
(6,4)
(7,4)
(0,5)
(1,5)
(2,5)
(3,5)
(4,5)
(5,5)
(6,5)
(7,5)
(0,6)
(1,6)
(2,6)
(3,6)
(4,6)
(5,6)
(6,6)
(7,6)
(0,7)
(1,7)
(2,7)
(3,7)
(4,7)
(5,7)
(6,7)
(7,7)
4: Building an interactive application Midlet with MIDP
Page: 4
Complementary Mobility
Building an interactive application Midlet with MIDP
Every time the player piece moves and hits an obstacle or reaches the bottom, the game grid is checked
to see if any adjoining pieces are found. If more than three Blocks are joined they are removed and the
grid adjusted. The process continues until no groups of three or more Blocks are found.
The interactive nature of this application uses the keyPressed event method then the getGameAction()
method defined in the Canvas class. This can return a number of event codes including: DOWN, LEFT,
RIGHT, FIRE and GAME_A to GAME_D codes. An alternative method would be to use the getKeyCode()
method which can return event codes for: KEY_NUM0 to KEY_NUM9, KEY_STAR and KEY_POUND.
4: Building an interactive application Midlet with MIDP
Page: 5
Complementary Mobility
Building an interactive application Midlet with MIDP
The following diagram shows the key methods of the checkState routine. This routine checks the game
play area for groups of three or more adjoining blocks, if they are found they are removed, blocks
moved down and the each block score.
This diagram shows the key methods for the main game loop. The player can move left, right and down.
Game over handling is also done here.
4: Building an interactive application Midlet with MIDP
Page: 6
Complementary Mobility
Building an interactive application Midlet with MIDP
Application Code
The following section shows the code for the myApp Class and the AppCanvas Class.
// A Simple Interactive Java Midlet
// import necessary libraries
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
// Class for myApp2 application
// Uses CommandListener
public class myApp2 extends MIDlet implements CommandListener
{
// static variables
public static Display
myDisplay;
public static Form
myForm;
public static Command
myCommandStart;
public static Command
myCommandExit;
public static Command
myCommandQuit;
private AppCanvas
appcanvas;
public static int
iMode;
public myApp2()
{
} // myApp2
public void startApp()
{
myDisplay = Display.getDisplay(this);
myCommandStart = new Command("Start", 1, 1);
myCommandExit = new Command("Exit", 1, 2);
myCommandQuit = new Command("Quit", 1, 3);
myForm = new Form("myApp2");
myForm.append("myApp2 application started");
myForm.addCommand(myCommandStart);
myForm.addCommand(myCommandExit);
iMode = 0;
myForm.setCommandListener(this);
myDisplay.setCurrent(myForm);
appcanvas = new AppCanvas(this);
appcanvas.addCommand(myCommandQuit);
appcanvas.setCommandListener(this);
} // startApp
public void commandAction(Command command, Displayable displayable)
{
// check if command button pressed
if(command == myCommandExit)
{
notifyDestroyed();
} // if
if(command == myCommandQuit)
{
myForm.addCommand(myCommandStart);
myForm.addCommand(myCommandExit);
iMode = 0;
} // if
// check if start button pressed
4: Building an interactive application Midlet with MIDP
Page: 7
Complementary Mobility
Building an interactive application Midlet with MIDP
if(command == myCommandStart)
{
// start application logic
myForm.removeCommand(myCommandStart);
myForm.removeCommand(myCommandExit);
appcanvas.start();
iMode = 1;
} // if
} // commandAction
// ---------// pauseApp
// ---------public void pauseApp()
{
} // pauseApp
// ---------// destroyApp
// ---------public void destroyApp(boolean flag)
{
} // destroyApp
} // myApp2
The code shown above includes a reference to a new Class called AppCanvas . This Class will contain
our interactive application. The code shown is as follows:
// A Simple Interactive Java Midlet
// import necessary libraries
import java.io.IOException;
import java.util.Random;
import javax.microedition.lcdui.*;
// Class for AppCanvas
// Uses Runnable
public class AppCanvas extends Canvas implements Runnable
{
// Define variables
Random random;
// random number
private myApp2 myParent;
// parent instance
private int score;
// player score
private boolean gameOver;
// game state
Thread appThread;
// game thread
int state;
// game state
private int blockSize;
// size of the game block
Image screenBuffer;
// draw the screen in a buffer
Graphics bg;
// get the graphics context of the screen
private int gameBoard[][]; // array to store the game board
int iLeft;
// number of blocks remaining
private int width;
// screen width
private int height;
// screen height
int playerX;
// player X position in game grid
int playerY;
// player Y position in game grid
Image blockImages[];
// array to store block images
Font fonthandle;
// fonthandle
String message;
// message to appear on screen
int playerdirection;
// direction the player is moving in
int xOffset;
// center the play area for larger screens
int iFound;
// stores number of matching blocks
4: Building an interactive application Midlet with MIDP
Page: 8
Complementary Mobility
Building an interactive application Midlet with MIDP
boolean bCheckState;
// if the game board needs to be checked
public AppCanvas(myApp2 inParent)
{
// set the random number generator
random = new Random();
// store the identity of the parent
myParent = inParent;
// define the game board, this will be 8x8
gameBoard = new int[8][8];
// create an array to store the 5 block images
blockImages = new Image[5];
// get the screen height and width
height = getHeight();
width = getWidth();
// create the offscreen buffer matching the screen width and height
screenBuffer = Image.createImage(width, height);
// get the graphics context of the offscreen buffer
bg = screenBuffer.getGraphics();
// get a font handle
fonthandle = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
// attempt to load suitable graphics depending on the screen size
try
{
if( width <= 108)
{
// smaller graphics for a smaller screen
blockImages[0] = Image.createImage("/images/sm1.png");
blockImages[1] = Image.createImage("/images/sm2.png");
blockImages[2] = Image.createImage("/images/sm3.png");
blockImages[3] = Image.createImage("/images/sm4.png");
blockImages[4] = Image.createImage("/images/sm5.png");
blockSize = 9;
// set the size of the game blocks
} else
{
// larger graphics for a larger screen
blockImages[0] = Image.createImage("/images/big1.png");
blockImages[1] = Image.createImage("/images/big2.png");
blockImages[2] = Image.createImage("/images/big3.png");
blockImages[3] = Image.createImage("/images/big4.png");
blockImages[4] = Image.createImage("/images/big5.png");
blockSize = 20;
// set the size of the game blocks
} // if
} // try
catch(IOException ioexception) { }
// ensure the game area is centered
xOffset = ( width - ( blockSize * 8 ) ) / 2;
} // AppCanvas constructor
private void dropBlocks()
{
boolean reloop;
reloop = true;
while (reloop)
{
4: Building an interactive application Midlet with MIDP
Page: 9
Complementary Mobility
Building an interactive application Midlet with MIDP
reloop = false;
for ( int y = 7; y > 1; y-- )
for ( int x = 0; x < 8; x++ )
if ( gameBoard[y][x] == 0 && gameBoard[y-1][x] != 0 )
{
gameBoard[y][x] = gameBoard[y-1][x];
gameBoard[y-1][x] = 0;
reloop = true;
}
}
}
private void resetGameBoard()
{
for ( int y = 0; y < 8; y++ )
for ( int x = 0; x < 8; x++ )
if ( gameBoard[y][x] > 99 ) gameBoard[y][x] -= 100;
}
private void scoreGameBoard()
{
for ( int y = 0; y < 8; y++ )
for ( int x = 0; x < 8; x++ )
if ( gameBoard[y][x] > 99 )
{
gameBoard[y][x] = 0;
iLeft--;
}
}
private void checkBlock(int y, int x, int iBlock)
{
gameBoard[y][x] = gameBoard[y][x] + 100;
iFound++;
if ( x < 7 && gameBoard[y][x+1] == iBlock )
{
checkBlock(y,x+1,iBlock);
}
if ( x > 0 && gameBoard[y][x-1] == iBlock )
{
checkBlock(y,x-1,iBlock);
}
if ( y > 1 && gameBoard[y-1][x] == iBlock )
{
checkBlock(y-1,x,iBlock);
}
if ( y < 7 && gameBoard[y+1][x]
{
checkBlock(y+1,x,iBlock);
}
}
== iBlock )
private void checkState()
{
// check the grid to see if any groups of three or more exist
iFound = 0;
for ( int y = 7; y > 0; y-- )
for ( int x = 0; x< 8; x++ )
{
iFound = 0;
if ( gameBoard[y][x] > 0) checkBlock(y,x,gameBoard[y][x]);
if ( iFound > 2 )
{
score = score + ( iFound * 10 );
scoreGameBoard();
dropBlocks();
4: Building an interactive application Midlet with MIDP
Page: 10
Complementary Mobility
Building an interactive application Midlet with MIDP
}
else
resetGameBoard();
}
if ( gameBoard[playerY][playerX] == 0 )
{
// get a new block
playerX = 0;
playerY = 0;
gameBoard[playerY][playerX] = chooseBlock();
playerdirection = 0;
}
}
private void clearBlock(int xpos, int ypos)
{
// clear the game board
gameBoard[ypos][xpos] = 0;
} // method clearBlock
// choose a random block from the 5 available
private int chooseBlock()
{
// choose a random block type
int iBlock = (random.nextInt() % 5);
if ( iBlock < 1 ) iBlock = iBlock * -1;
iBlock += 1;
return iBlock;
} // method chooseBlock()
// Update the screen from the buffer
public void paint(Graphics g)
{
g.drawImage(screenBuffer, 0, 0, Graphics.TOP|Graphics.LEFT);
} // method paint()
// setup the game board
public void initBoard()
{
boolean blockDiff;
// clear the game grid
for ( int i = 0; i < 8; i++ )
for(int j = 0; j < 8; j++)
gameBoard[i][j] = 0;
// setup the game grid with random blocks
// remember that the grid is stored as (y,x) but drawn as (x,y)
for(int i = 3; i < 8; i++)
for(int j = 0; j < 8; j++)
{
blockDiff = true;
// ensure no blocks appear together
while(blockDiff)
{
blockDiff = false;
gameBoard[i][j] = chooseBlock();
// check for blocks together
if(j != 0) if(gameBoard[i][j] == gameBoard[i][j - 1]) blockDiff =
true; //
check left
if(gameBoard[i][j] == gameBoard[i - 1][j]) blockDiff = true; //
check up
4: Building an interactive application Midlet with MIDP
Page: 11
Complementary Mobility
Building an interactive application Midlet with MIDP
} // while
} // for
// place the player block
gameBoard[playerY][playerX] = chooseBlock();
gameBoard[5][0]
gameBoard[5][1]
gameBoard[6][0]
gameBoard[7][0]
gameBoard[7][1]
=
=
=
=
=
1;
1;
1;
1;
1;
} // method initBoard()
private void updateScreen()
{
// clear the offscreen buffer
bg.setGrayScale(255);
bg.fillRect(0, 0, width, height);
// show the player score
showScore();
// draw the blocks
for(int i = 0; i < 8; i++)
for(int j = 0; j < 8; j++)
if(gameBoard[i][j] != 0)
bg.drawImage(blockImages[gameBoard[i][j]
-
1],
xOffset
+
(j
*
blockSize),
height-((8-i) * blockSize), Graphics.TOP|Graphics.LEFT);
if ( message != "" ) showMessage();
// force a screen update
repaint();
} // method updateScreen()
public void resetApp()
{
// set the initial score
score = 0;
// set the play state to by in play
state = 1;
// reset the game board
initBoard();
// set the number of remaining game blocks
iLeft = 40;
// reset the player block position
playerX = 0;
playerY = 0;
// clear any message
message = "";
} // method resetApp()
// show the player score at the top of the screen
private void showScore()
{
bg.setGrayScale(0);
4: Building an interactive application Midlet with MIDP
Page: 12
Complementary Mobility
Building an interactive application Midlet with MIDP
bg.drawString("" + score + " Left:" + iLeft , 1, 1, Graphics.TOP|Graphics.LEFT);
} // method showScore()
// show a message centered on the screen
private void showMessage()
{
bg.setGrayScale(0);
bg.drawString(message, (width >> 1) , ( height >> 1 ) , 0x10 | 1);
// change the game state
} // method showMessage()
// detect a key press and action it
public void keyPressed(int keyCode)
{
switch(getGameAction(keyCode))
{
case LEFT:
playerdirection = 1;
break;
case RIGHT:
playerdirection = 2;
break;
case DOWN:
playerdirection = 3;
break;
} // switch
} // method keyPressed
// keep running the application until it is terminated
public void run()
{
// setup the game
resetApp();
// loop this routine until game terminated
while( state != 99 )
{
int iTemp = gameBoard[playerY][playerX];
boolean bValidMove = false;
bCheckState = false;
switch(state)
{
default:
break;
// play the game
case 1:
switch (playerdirection)
{
// by default do nothing
default:
break;
// move player left
case 1:
gameBoard[playerY][playerX] = 0;
playerX--;
if ( playerX < 0 ) playerX = 7;
gameBoard[playerY][playerX] = iTemp;
playerdirection = 0;
break;
// move player right
case 2:
gameBoard[playerY][playerX] = 0;
4: Building an interactive application Midlet with MIDP
Page: 13
Complementary Mobility
Building an interactive application Midlet with MIDP
playerX++;
if ( playerX > 7 ) playerX = 0;
gameBoard[playerY][playerX] = iTemp;
playerdirection = 0;
break;
// move player down
case 3:
gameBoard[playerY][playerX] = 0;
playerY++;
bValidMove = true;
// check if player reached baseline
if ( playerY > 7 )
{
playerY--;
playerdirection = 0;
gameBoard[playerY][playerX] = iTemp;
// increase outstanding block count
iLeft++;
// get a new block
playerX = 0;
playerY = 0;
iTemp = chooseBlock();
bCheckState = true;
}
else
{
// check if player reached another block
if ( gameBoard[playerY][playerX] != 0 )
{
playerY--;
playerdirection = 0;
gameBoard[playerY][playerX] = iTemp;
// if player cannot drop block then end game
if ( playerY == 0 )
{
bValidMove = false;
state = 97;
}
else
{
bValidMove = false;
// increase outstanding block count
iLeft++;
// get a new block
playerX = 0;
playerY = 0;
iTemp = chooseBlock();
}
bCheckState = true;
}
}
gameBoard[playerY][playerX] = iTemp;
if ( bValidMove ) score++;
break;
} // switch
if(iLeft == 0) state = 98; // change to win state
break;
// end the game - lose
case 97:
message = "Game Over";
4: Building an interactive application Midlet with MIDP
Page: 14
Complementary Mobility
Building an interactive application Midlet with MIDP
state = 99;
break;
// end the game - win
case 98:
message = "Well Done";
state = 99;
break;
}
// update the screen backbuffer then paint the screen
updateScreen();
// check if any connecting blocks appear
if ( bCheckState) checkState();
} // while
} // method run()
synchronized void start()
{
appThread = new Thread(this);
appThread.start();
} // method start
} // Class AppCanvas
4: Building an interactive application Midlet with MIDP
Page: 15
Complementary Mobility
Building an interactive application Midlet with MIDP
Analysing the program code
The interactive application is run as a thread from the main harness program previously developed.
Initially an instance of the AppCanvas class is created and the Quit command (and its associated listener)
is attached to it.
// create an instance of Class AppCanvas
appcanvas = new AppCanvas(this);
// add a Quit button
appcanvas.addCommand(myCommandQuit);
appcanvas.setCommandListener(this);
When ready, the thread is started using the start() method.
// start the thread
appcanvas.start();
The AppCanvas constructor sets about creating our interactive world including getting the display
dimensions as previously discussed, creating an offscreen buffer and loading in the game graphics.
Game graphics are loaded using the Image Class and the createImage method. Instances of Class Image
exist independently from any device and simply hold image information, in this way they can be
transferred to either a form, a canvas, or in our instance to an offscreen buffer.
// Load an image
blockImages[0] = Image.createImage("/images/sm1.png");
As we intend to employ an offscreen buffer we declare it in our constructor but first we get the device
display height and wdith so we can create a new instance of the Image Class, which we will call
screenBuffer.
// get the screen height and width
height = getHeight();
width = getWidth();
// create the offscreen buffer matching the screen width and height
screenBuffer = Image.createImage(width, height);
To make this buffer useful we need to associate a Graphics object to our Image. We use the getGraphics
method of the Image Class which creates a Graphics object pointing to our previously created image.
Anything plotted outside of the image area will be clipped and the co-ordinate system will be top left.
// get the graphics context of the offscreen buffer
bg = screenBuffer.getGraphics();
The application starts by receiving a request from the thread manager and running its run() method. The
first thing this method does is to reset the game variables including the score, the game state and clears
the game board ready to play.
4: Building an interactive application Midlet with MIDP
Page: 16
Complementary Mobility
Building an interactive application Midlet with MIDP
The run() method then loops until the gamestate reaches the value of 99 at which point the thread
terminates. If the game state is Game Over then a message is displayed to the user and the game state
changes to 99. The other state, the Play State, has three play states, that of moving the player block left,
moving right and allowing a block to fall. At the completion of each of these states the offscreen image
is painted to the current device and, if necessary, the game board is checked.
// Update the screen from the buffer
public void paint(Graphics g)
{
g.drawImage(screenBuffer, 0, 0, Graphics.TOP|Graphics.LEFT);
} // method paint()
Using an offscreen buffer means that drawing to the screen becomes a fairly simple process of selecting
a color then plotting either a pixel, text, or some other item onto the buffer. We always start the
offscreen drawing process by setting the current colour to white then filling the offscreen buffer –
essentially clearing it.
bg.setGrayScale(255);
bg.fillRect(0, 0, width, height);
Drawing images on the offscreen buffer is as simple as specifying the image to draw and its location in
the buffer relative to the top left.
bg.drawImage(images, X Position, Y Position, Graphics.TOP|Graphics.LEFT);
Moving the player block left or right is fairly self explanatory though its worth mentioning that if the
player block goes beyond the left or right boundary then it reappears at the other end of the play area.
Reporting which direction the game player wishes to move is the job of the keyPressed method which
detects a key press then calls the getGameAction method to determine which key the player has
pressed – any unrecognised keys report a zero. We detect the MIDP reserved words LEFT, RIGHT and
DOWN as different devices may use different keys to represent actions.
// detect a key press and action it
public void keyPressed(int keyCode)
{
switch(getGameAction(keyCode))
{
} // switch
} // method keyPressed
4: Building an interactive application Midlet with MIDP
Page: 17
Complementary Mobility
Building an interactive application Midlet with MIDP
Testing the interactive application
Once you have placed the files in their appropriate folders you should click on the Build button the
toolbar. If the “Build complete” message does not appear then check you have placed the files correctly
and that the code is typed as shown in the listing. Onscreen messages may guide you to fix any
compilation problems.
You can now click on the Run button on the menu and the interactive application Midlet should run.
Click the Start soft button and the application should look similar to that shown in the following diagram
– (if running under the default emulator).
4: Building an interactive application Midlet with MIDP
Page: 18
Download