The menu item sprite images are words written on a transparent background so the game screen is visible between and around the letters and the letters appear in black or blue depending o
Trang 1Figure 10-5. The image that gives the three frames of the sparkling animation as horizontal strips
If you want an animated menu without drawing the entire thing from scratch, it is possible
to add animated CustomItems to an lcdui Form However, in that case you have to implement
the CustomItem yourself, so it’s really no easier than painting the whole menu as in the Dungeon
example Another possibility in MIDP 3 is to associate an animated image with each item in
a list But again the platform decides how to display the list and will insist on displaying the
corresponding strings in one of the platform’s fonts and without giving you the option of
plac-ing a background image behind it So the built-in lcdui widgets simply don’t give you control
over the look of the menu When you write the code yourself to draw the whole menu onto
a Canvas, you can give it whatever look you want Figure 10-6 shows the result for the improved
Dungeon example
Figure 10-6. The Dungeon game in menu mode on a small-screen emulator
The menu works by having a menu softkey switch the DungeonCanvas in and out of menumode (just toggling a boolean field myMenuMode in the class) When in menu mode, the current
game canvas is painted on the screen as usual, but then the menu items are painted on top as
sprites The menu item sprite images are words written on a transparent background (so the
game screen is visible between and around the letters) and the letters appear in black or blue
depending on whether the item is currently the focused item (as discussed in the section
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 2“Modifying Image Colors and Transparency” earlier in this chapter) and the animated sparklingsprite is painted behind the focused item The focus can be moved from one menu item toanother using the arrow keys, and the focused item is the item that will be selected when theplayer clicks Select In menu mode the game thread continues advancing as usual and query-ing for keystrokes, but the keystroke information is interpreted by the menu command codeand the game loop advances the sparkling animation of the star sprite instead of advancingthe game ticks and game animation All of this is seen in Listing 10-8
// (constant after initialization)/**
* the top corner x coordinate according to this
* object's coordinate system:
*/
static int CORNER_X = 0;
/**
* the top corner y coordinate according to this
* object's coordinate system:
*/
static int CORNER_Y = 0;
/**
* the width of the portion of the screen that this
* canvas can use
*/
static int DISP_WIDTH;
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 3* the height of the portion of the screen that this
* canvas can use
Trang 4* The number of ticks on the clock the last time the
* time display was updated
* This is saved to determine if the time string needs
* to be recomputed
*/
int myDisplayGameTicks = 0;
/**
* the number of game ticks that have passed since the
* beginning of the game
*/
int myGameTicks = myDisplayGameTicks;
/**
* An array of number sprites to hold the digit images
* for the time display
* The button to go to the next board
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 5Command myOkCommand;
// menu-related fields
Trang 6* This is called when the game ends
*/
void setGameOver() {myGameOver = true;
myDungeon.pauseApp();
}/**
* Get the DungeonManager
*/
DungeonManager getManager() {return myManager;
}/**
* Find out if the game has ended
*/
static boolean getGameOver() {
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 7}/**
* Get the Customizer
*/
public Customizer getCustomizer() {return myCustomizer;
}// -// initialization and game state changes
/**
* Constructor sets the data, performs dimension calculations,
* and creates the graphical objects
}if((DISP_WIDTH < 150) || (DISP_HEIGHT < 170)) {throw(new Exception("Screen too small"));
}if((DISP_WIDTH > 375) || (DISP_HEIGHT > 375)) {throw(new Exception("Screen too large"));
}// create the class that handles the differences among// the various platforms
myCustomizer = new Customizer(DISP_WIDTH, DISP_HEIGHT);
// create the LayerManager (where all of the interesting // graphics go!) and give it the dimensions of the // region it is supposed to paint:
if(myManager == null) {myManager = new DungeonManager(CORNER_X, CORNER_Y, DISP_WIDTH, DISP_HEIGHT, myCustomizer, this);
} }
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 8* Once the customizer has been initialized, this
* method loads and initializes the graphical objects
* for the timer and the menu
*/
void start() throws IOException {myGameOver = false;
// initialize the graphics for the timeclock:
Image numberImage = myManager.getNumberImage();
int width = numberImage.getWidth() / 11;
int height = numberImage.getHeight();
for(int i = 0; i < 5; i++) {myNumberSprites[i] = new Sprite(numberImage, width, height);
myNumberSprites[i].setPosition(width*i, 0);
}// frame 10 is the colon:
myNumberSprites[2].setFrame(10);
// if the customizer identifies the platform as // one we have keycode data for, we can implement// the softkeys with images
if(myCustomizer.useSoftkeys()) {setFullScreenMode(true);
myDisplay.setCurrent(this);
// initialize the menu graphics:
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 9int width = tempImage.getWidth();
int height = tempImage.getHeight();
Sprite retObj = new Sprite(ColorChanger.createFocused(tempImage, OPAQUE_BLACK, OPAQUE_BLUE), width, height);
retObj.defineReferencePixel(width/2, height/2);
return(retObj);
}/**
* sets all variables back to their initial positions
*/
void reset() throws Exception {// most of the variables that need to be reset // are held by the LayerManager:
Trang 10* sets all variables back to the positions
* from a previously saved game
*/
void revertToSaved() throws Exception {// most of the variables that need to be reset // are held by the LayerManager, so we
// prompt the LayerManager to get the // saved data:
myGameOver = false;
myDisplayGameTicks = myManager.revertToSaved();
}/**
* save the current game in progress
*/
void saveGame() throws Exception {myManager.saveGame(myDisplayGameTicks);
}/**
* clears the key states
*/
void flushKeys() {getKeyStates();
}/**
* Switch to showing the game action menu
*/
void setMenuMode() {myMenuMode = !myMenuMode;
}/**
* If the game is hidden by another app (or a menu)
* ignore it since not much happens in this game
* when the user is not actively interacting with it
Trang 11// graphics methods
try {myManager.paint(g);
} catch(Exception e) {myDungeon.errorMsg(e);
return;
}// the timer is painted on top of // the game graphics:
for(int i = 0; i < 5; i++) {myNumberSprites[i].paint(g);
}// paint the menu on if in menu mode:
if(myMenuMode) {int y = MENU_BUFFER;
for(int i = 0; i < myMenuVector.size(); i++) {Sprite item = (Sprite)(myMenuVector.elementAt(i));
if(i == myFocusedIndex) {myStars.setRefPixelPosition(DISP_WIDTH / 2, y);
myStars.paint(g);
item.setFrame(FOCUSED);
} else {item.setFrame(UNFOCUSED);
y += myStars.getHeight()/2;
y += MENU_BUFFER;
}}if(myCustomizer.useSoftkeys()) {g.drawImage(myExit, 2, DISP_HEIGHT - 2, Graphics.BOTTOM|Graphics.LEFT);
if(myGameOver) {g.drawImage(myOk, DISP_WIDTH - 2, DISP_HEIGHT - 2, Graphics.BOTTOM|Graphics.RIGHT);
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 12} else {g.drawImage(myMenu, DISP_WIDTH - 2, DISP_HEIGHT - 2, Graphics.BOTTOM|Graphics.RIGHT);
}}// write "Next Board" when the user finishes a board:
if(myGameOver) {myNext.setFrame(UNFOCUSED);
myNext.setRefPixelPosition(DISP_WIDTH / 2, DISP_HEIGHT / 2);
myNext.paint(g);
}}/**
* a simple utility to make the number of ticks look
* like a time
*/
public void setTimeSprites() {// we advance the display ticks once // for every twenty game ticks because// there are twenty frames per second:
if(myGameTicks % 20 == 0) {// the number sprite is designed so that // the frame number corresponds to the // actual digit:
/**
* update the display
*/
void updateScreen() {if(! myMenuMode) {myGameTicks++;
setTimeSprites();
} else {// in menu mode the game doesn't advance
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 13// but the sparking animation behind the // selected item must advance:
myStars.nextFrame();
}// paint the display:
try {paint(getGraphics());
flushGraphics(CORNER_X, CORNER_Y, DISP_WIDTH, DISP_HEIGHT);
} catch(Exception e) {myDungeon.errorMsg(e);
}}/**
* Respond to keystrokes
*/
public void checkKeys() { if(! myGameOver) {// determine which moves the user would like to make:
int keyState = getKeyStates();
if(myMenuMode) {menuAction(keyState);
} else {int vertical = 0;
int horizontal = 0;
if((keyState & LEFT_PRESSED) != 0) {horizontal = -1;
} if((keyState & RIGHT_PRESSED) != 0) {horizontal = 1;
}if((keyState & UP_PRESSED) != 0) {vertical = -1;
} if((keyState & DOWN_PRESSED) != 0) {// if the user presses the down key, // we put down or pick up a key object// or pick up the crown:
myManager.putDownPickUp();
} // tell the manager to move the player // accordingly if possible:
myManager.requestMove(horizontal, vertical);
}}}
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 14}// change which item is selected in // response to up and down:
if((keyState & UP_PRESSED) != 0) {if(myFocusedIndex > 0) {
myFocusedIndex ;
}} if((keyState & DOWN_PRESSED) != 0) {if((myFocusedIndex + 1) < myMenuVector.size()) {myFocusedIndex++;
}}} catch(Exception e) {myDungeon.errorMsg(e);
}}/**
* Respond to softkeys
* The keystates value won't give information
* about softkeys, so the keypressed method
* must be implemented separately:
*/
public void keyPressed(int keyCode) {int softkey = myCustomizer.whichSoftkey(keyCode);
if(softkey == Customizer.SOFT_LEFT) {// left is exit:
Trang 15// or advances to the next board if a board// is done:
try {if(myGameOver) {reset();
flushKeys();
myDungeon.resumeGame();
} else {setMenuMode();
}} catch(Exception e) {myDungeon.errorMsg(e);
}}}/**
* Respond to softkeys in the case where
* lcdui commands are used instead of custom
} else if(c == myOkCommand) {removeCommand(myOkCommand);
}}}
You can see that another advantage of this version over the version in Chapter 5 is that thetimer is also drawn with a set of five sprites (all using the same image) instead of being drawn
with one of the platform’s built-in fonts onto an ugly black bar drawn across the bottom of the
screen Figure 10-7 shows the difference
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 16Figure 10-7. The difference in how the timer number appears in the two versions
The trickiest part of the menu code in Listing 10-8 is the implementation of the softkeys,explained in the next section
Implementing Softkeys
Nearly every MIDP device uses a system of softkeys to drive its built-in GUI A softkey is
a physical button on the device whose function changes depending on the context The buttoncorresponding to the softkey is generally placed right next to the screen so that the programcan display the current label for it such as Exit, Options, or OK A typical platform has twosoftkeys—a right softkey and a left softkey—at the base of the screen
When writing your own custom GUI, naturally you want to be able to implement the softkeyfunctionality so that you can be sure that the softkey labels match the look of the rest of thegraphical components Unfortunately, the designers of MIDP decided that they didn’t want tomake it easy for you to do this The philosophy seems to be that the softkeys are a platform-specific implementation detail that you don’t need to know about, and if you start fiddlingwith them, you can mess up the platform’s consistent user experience by perhaps putting yourExit key on the left when the platform normally puts it on the right, or you might assume thesoftkeys are at the bottom of the screen when for a particular platform they’re at the top SoMIDP encapsulates softkeys (and possibly other related input features) in Command objects andsays in essence “We’ll handle them—you don’t touch them.”
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 17However, since so many devices use this same consistent design of placing the softkeys atthe bottom left and bottom right of the screen, it’s natural to want to try to implement them
yourself to create a consistent, attractive look for your game It would have been nice if the
designers of MIDP had at least thrown in some minimal support such as a system property
you can query to determine whether the platform uses standard (bottom left and bottom
right) softkeys, and then provide the corresponding key constants in the GameCanvas class As it
is, it’s possible to receive and interpret the softkey events, but you have to do some additional
work, and the resulting code isn’t terribly portable Essentially you have to customize the JAR
file for every target device (or at least every manufacturer) that you’d like to use custom
soft-keys on
Here’s how to do it If you look at Listing 10-8, you’ll see that in addition to querying for keystates (as in the corresponding class in Chapter 5), the keyPressed() method is also implemented
This is because the return value of the getKeyStates() method only returns information about
the standard Java key constants However, the AMS calls the keyPressed() method with the
platform’s raw keycode integer corresponding to the key that was pressed Since these keycodes
vary from one device to the next, you usually follow with the getGameAction class to translate
the platform-specific keycode into a recognizable Java constant (as explained in the section
“Using the Graphics and Canvas Classes” from Chapter 2) But if you happen to know the
cur-rent device’s codes for the left and right softkeys, this is where you use that information
Figuring out what keycodes your game should be using at any given moment is a two-stepprocess: (1) identify the platform, and (2) load and use the correct keycode values for the current
platform Identifying the platform is a challenge all its own, discussed in the “Identifying the
Platform” sidebar earlier Then figuring out the keycodes for all of your target devices is a
sep-arate challenge Some manufacturers (Sony Ericsson, for example) publish developer guides
that contain the keycodes Additionally, there are online databases listing the keycodes for many
popular devices, such as the device database on the J2ME Polish web site (see the “Using J2ME
Polish” sidebar)
Many devices follow the WTK’s example for their keycode mapping: -6 for the left softkeyand -7 for the right softkey (These are the values used by Sony Ericsson, for example.) But many
don’t My Sagem my700x, for example, uses exactly the reverse
Once you know the keycodes for the current device, implementing the softkeys is a snap
As you can see in Listing 10-8, you set your canvas to full-screen mode, then paint on the
soft-key labels in the lower right and left corners of the screen, and handle the softsoft-key actions in
the keyPressed() method
However, it’s good to provide a fallback for the case where the game is running on a devicethat you can’t identify and/or don’t have the keycodes for In this case, we can still use the cus-
tom menu code, but the menu screen is put up and down using standard lcdui Command objects,
implemented in the commandAction() method of Listing 10-8 It’s not quite as pretty, but it’s not
the end of the world Figure 10-8 shows what that looks like
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com