Programming Java 2 Micro Edition for Symbian OS 2004 (779882), страница 56
Текст из файла (страница 56)
The GameMIDlet displayPuzzleCanvas() methodis invoked to display the image.public void displayPuzzleCanvas(byte[] imageData) {Image image = Image.createImage(imageData, 0, imageData.length);puzzleCanvas = new PuzzleCanvas(this, image);display.setCurrent(puzzleCanvas);}The displayPuzzleCanvas() method creates the Image and anew PuzzleCanvas instance and displays it.5.4.6The PuzzleCanvas ClassThe game logic is encapsulated in the PuzzleCanvas Class:package picturepuzzle;import javax.microedition.lcdui.*;import javax.microedition.lcdui.game.*;import java.util.*;THE PICTURE PUZZLE307/*** The game Canvas. Displays the randomized image as a 4x4 grid of* tiles. Allows the user to re-arrange the tiles.
Indicates when the* correct arrangement has been arrived at and the game is over.*/public class PuzzleCanvas extends Canvas implements CommandListener {static final int IMAGE_WIDTH = 160;static final int IMAGE_HEIGHT = 120;static final int COLS = 4;static final int ROWS = 4;private TiledLayer imageLayer;private Image image;private int cursorX, cursorY;//position coordinates of cursorprivate GameMIDlet midlet;private boolean doublePaired;private Command exitCommand;private Command hintCommand;private Command newCommand;private int firstBlock = 0;private int secondBlock = 0;private int firstCol = 0;private int firstRow = 0;private int secondCol = 0;private int secondRow = 0;// Creates the scrambled puzzle from an image.public PuzzleCanvas(GameMIDlet midlet, Image image) {super();this.midlet = midlet;this.image = image;exitCommand = new Command("Exit", Command.EXIT, 1);hintCommand = new Command("Hint", Command.SCREEN, 2) ;newCommand = new Command("New game", Command.SCREEN, 2);addCommand(exitCommand);addCommand(hintCommand);setCommandListener(this);createBoard();cursorX = getWidthDiff() / 2;cursorY = getHeightDiff() / 2;imageLayer.setPosition(getWidthDiff() / 2, getHeightDiff() / 2);doublePaired = true;}public int getWidthDiff() {return getWidth() - IMAGE_WIDTH;}public int getHeightDiff() {return getHeight() - IMAGE_HEIGHT;}// randomize the order of tiles in the image layer.private void createBoard() {imageLayer = new TiledLayer(COLS, ROWS, image, IMAGE_WIDTH/COLS,IMAGE_HEIGHT/ROWS);Random ran = new Random();Vector v = new Vector(ROWS*COLS);boolean b = true;int i;// get integer numbers from 1 to ROWS*COLS in random order308MIDP 2.0 CASE STUDIESwhile (b) {i = ran.nextInt()%(ROWS*COLS)+1;if (i > 0 && i <= (ROWS*COLS)) {if ( !v.contains(new Integer(i)) ) {v.addElement(new Integer(i));}if (v.size() == ROWS*COLS) {b = false;}}}for (int m = 0; m < ROWS*COLS; m++) {int integer = ( (Integer) v.elementAt(m)).intValue();imageLayer.setCell( m/ROWS ,m%ROWS, integer);}}// Paints the current TiledLayer arrangement and draws the cursor.// Also indicates "Game over" when the game is completed.public void paint(Graphics g) {g.setColor(255, 255, 255);// Paint a white backgroundg.fillRect(0, 0, getWidth(), getHeight());imageLayer.paint(g);g.setColor(255, 0, 0);drawFrame(cursorX, cursorY, g);if (isWinning()){g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD,Font.SIZE_LARGE));g.drawString("Game Over!!", getWidth() / 2, getHeight() / 2,Graphics.HCENTER | Graphics.TOP);}}// Responds to movement of the Joystick.public void keyPressed(int keyCode) {int key = getGameAction(keyCode);if (key == LEFT) {moveLeft();} else if (key == RIGHT) {moveRight();} else if (key == UP) {moveUp();} else if (key == DOWN) {moveDown();} else if (key == FIRE && !doublePaired) {setSecondBlock();if (isWinning()) {addCommand(newCommand);}} else if (key == FIRE && doublePaired) {setFirstBlock();}repaint();}// Ascertains whether the current arrangement of tiles is equal to the//original image and hence the game has been successfully completed.public boolean isWinning() {int count = 1 ;for (int row = 0 ; row < imageLayer.getRows() ; row++) {for (int col = 0 ; col < imageLayer.getColumns() ; col++) {THE PICTURE PUZZLE309if (imageLayer.getCell(col, row) != count) {return false ;}count++;}}return true;}// Draws cursor.private void drawFrame(int x, int y, Graphics g) {g.drawRect(x, y, imageLayer.getCellWidth(),imageLayer.getCellHeight());}// Moves cursor one tile up.public void moveUp() {cursorY = cursorY - imageLayer.getCellHeight();if (cursorY < getHeightDiff() / 2) {cursorY = getHeightDiff() / 2;}}// Moves cursor one tile down.public void moveDown() {cursorY = cursorY + imageLayer.getCellHeight();int yMax = IMAGE_HEIGHT + getHeightDiff()/2 imageLayer.getCellHeight();if (cursorY > yMax) {cursorY = yMax;}}// Moves cursor one tile left */public void moveLeft() {cursorX = cursorX - imageLayer.getCellWidth();if (cursorX < getWidthDiff() / 2) {cursorX = getWidthDiff() / 2;}}// Moves cursor one tile right.public void moveRight() {cursorX = cursorX + imageLayer.getCellWidth();if ( cursorX > IMAGE_WIDTH + getWidthDiff() / 2 imageLayer.getCellWidth()) {cursorX = IMAGE_WIDTH + getWidthDiff() / 2 imageLayer.getCellWidth();}}// Gets the initial tile that the user has selected for transposition.public void setFirstBlock() {firstCol = (cursorX - getWidthDiff() / 2) /imageLayer.getCellWidth();firstRow = (cursorY - getHeightDiff() / 2) /imageLayer.getCellHeight();firstBlock = imageLayer.getCell(firstCol, firstRow);doublePaired = false;}// Gets the destination tile selected by the user.310MIDP 2.0 CASE STUDIES// Then interchanges the initial and destination tiles.public void setSecondBlock() {secondCol = (cursorX - getWidthDiff() / 2) /imageLayer.getCellWidth();secondRow = (cursorY - getHeightDiff() / 2) /imageLayer.getCellHeight();secondBlock = imageLayer.getCell(secondCol, secondRow);// interchange two cellsimageLayer.setCell(firstCol, firstRow, secondBlock);imageLayer.setCell(secondCol, secondRow, firstBlock);doublePaired = true;}public void commandAction(Command command, Displayable displayable) {if (command == exitCommand) {midlet.exit();} else if (command == hintCommand){new Thread() {//to avoid blocking despatcherpublic void run(){midlet.displayHintCanvas(image);}}.start();} else if (command == newCommand) {midlet.displayChoiceForm();image = null;midlet = null;}}}The createBoard() method takes the captured image and rearranges it.
It uses the image to create a 4 × 4 TiledLayer. Re-arrangingthe image then simply becomes a matter of successively calling the setCell() method of the TiledLayer. The PuzzleCanvas handles alluser interaction itself either in the keyPressed() method (inheritedfrom Canvas) or the commandAction() method mandated by theCommandListener interface which PuzzleCanvas implements. ThekeyPressed() method listens for arrow key (or joystick, in the case ofthe Nokia 6600) events and moves the cursor appropriately.In response to FIRE key events (choosing ‘‘Select’’ in the WirelessToolkit or depressing the joystick on the Nokia 6600), the applicationselects the tile from the first FIRE event and transposes it with the onefrom the second FIRE event.The commandAction() method provides options to exit the application, start a new game (upon successful completion of the current game)and display a hint screen.The hint option calls the GameMIDlet method displayHintCanvas(), which creates a HintCanvas instance displaying the original(unscrambled) image as a hint to the user.
Note that this method shouldbe called from within a new Thread so that the commandAction()method can return quickly and avoid blocking the single VM eventdispatcher Thread.THE PICTURE PUZZLE311The exitCommand option calls the exit() method of GameMIDlet:public void exit(){try{rms.closeRecordStore();}catch(ApplicationException ae){}if(capturer != null){capturer.discardPlayer();}notifyDestroyed();}This closes the record store and discards the player, releasing itsresources, then calls notifyDestroyed() to indicate to the AMS thatthe MIDlet has moved into the DESTROYED state and can be reclaimed.5.4.7The RMSHandler ClassThe last class we should describe is the RMSHandler (shown below)which handles loading previous images from storage, saving a new imageto storage and deleting images from storage.package picturepuzzle;import javax.microedition.rms.*;import java.io.*;import java.util.*;// Used to store images in RMS storage.// Stores images in the IMAGE RecordStore.
Creates an INDEX record store// to store the name of the image and its record id for easy retrieval.public class RMSHandler {//Name of record store for storing imagespublic static final String IMAGE_RECORD_STORE = "IMAGES";//Name of record store for storing index entriespublic static final String INDEX_RECORD_STORE = "KEYS";private RecordStore indexRecordStore;private RecordStore imageRecordStore;private Hashtable hashTable;public RMSHandler(){hashTable = new Hashtable();}//Opens IMAGE and INDEX record storespublic void openRecordStore() throws ApplicationException {try {imageRecordStore =RecordStore.openRecordStore(IMAGE_RECORD_STORE, true);indexRecordStore =RecordStore.openRecordStore(INDEX_RECORD_STORE, true);} catch (RecordStoreException rse) {312MIDP 2.0 CASE STUDIESthrow new ApplicationException("Unable to open record store",rse);}}//Closes IMAGE and INDEX record stores.public void closeRecordStore() throws ApplicationException {try {imageRecordStore.closeRecordStore();indexRecordStore.closeRecordStore();} catch (RecordStoreException rse) {throw new ApplicationException("Unable to close record store",rse);}}//Adds an entry to the INDEX storeprivate int addKey(String name, int recordID) throwsApplicationException {try {ByteArrayOutputStream baos = new ByteArrayOutputStream();DataOutputStream dos = new DataOutputStream(baos);dos.writeUTF(name);dos.writeInt(recordID);byte[] data = baos.toByteArray();int keyID = indexRecordStore.addRecord(data, 0, data.length);return keyID;} catch (IOException ioe) {throw new ApplicationException("Unable to add key to record store", ioe);} catch (RecordStoreException rse) {throw new ApplicationException("Unable to add key to record store", rse);}}//Deletes the index entry from the INDEX record store.private void deleteKey(int keyID) throws ApplicationException {try {indexRecordStore.deleteRecord(keyID);} catch (RecordStoreException rse) {throw new ApplicationException("Unable to delete key from record store", rse);}}//Adds Image data to IMAGE RecordStore.private int addImageRecord(byte[] data) throws ApplicationException {try {int recordID = imageRecordStore.addRecord(data, 0,data.length);return recordID;} catch (RecordStoreException rse) {throw new ApplicationException("Unable to add record to record store", rse);}}THE PICTURE PUZZLE313//Deletes Image data from IMAGE RecordStore.private void deleteImageRecord(int recordID) throwsApplicationException {try {imageRecordStore.deleteRecord(recordID);return;} catch (RecordStoreException rse) {throw new ApplicationException("Unable to delete record from record store", rse);}}//Adds an Image to the IMAGE RecordStore and its name and record ID//to the INDEX record store.public void addImageToStore(String name, byte[] imageData)throws ApplicationException {int[] recordIndices = new int[2];recordIndices[0] = addImageRecord(imageData);recordIndices[1] = addKey(name, recordIndices[0]);hashTable.put(name, recordIndices);}//Deletes image from IMAGE store and associated entry in INDEX storepublic void deleteImageFromStore(String name)throws ApplicationException {int[] recordIndices = (int[])hashTable.get(name);if (recordIndices != null) {deleteImageRecord(recordIndices[0]);deleteKey(recordIndices[1]);hashTable.remove(name);}}//Retrieves an Image from the IMAGE RecordStore.public byte[] retrieveImageFromStore(String name)throws ApplicationException {int[] recordIndices = (int[])hashTable.get(name);byte[] imageData = null;if (recordIndices != null) {try {imageData = imageRecordStore.getRecord(recordIndices[0]);}catch(RecordStoreException rse) {throw new ApplicationException("Unable to retrieve record from record store",rse);}}return imageData;}//Retrieves the names of images stored in the record store.public String[] retrieveStoredImageNames()throws ApplicationException {String[] entries = null;try {if (indexRecordStore.getNumRecords() == 0) {314MIDP 2.0 CASE STUDIESreturn null;}RecordEnumeration records =indexRecordStore.enumerateRecords(null, null, false);int numOfRecords = records.numRecords();int[][] recordIndices = new int[numOfRecords][2];entries = new String[numOfRecords];for (int i = 0; i < numOfRecords; i++) {int keyID = records.nextRecordId();byte[] data = indexRecordStore.getRecord(keyID);ByteArrayInputStream bais =new ByteArrayInputStream(data);DataInputStream dis = new DataInputStream(bais);String imageName = dis.readUTF();int recordID = dis.readInt();recordIndices[i][0] = recordID;recordIndices[i][1] = keyID;entries[i] = imageName;hashTable.put(imageName, recordIndices[i]);}return entries;} catch (IOException ioe) {throw new ApplicationException("Unable to read from record store", ioe);} catch (RecordStoreException rse) {throw new ApplicationException("Unable to read from record store", rse);}}}The RMS record store provides persistent storage of data in the formof records within a RecordStore.