Storing and Retrieving Data - Applying Data Storage to a Game
(Page 4 of 8 )
In this section, you’ll see how the example game works. As I mentioned previously, the game involves a character (in fact, a princess) exploring a dungeon that’s made up of a 16x16 grid (see Figure 5-1). To make it more interesting, the maze is a vertical cross-section of the dungeon, so the princess gets around by jumping up and falling down in addition to running around. Also, to add to the challenge, four keys on each board open eight locked doors. Each key is a different color and opens doors of the corresponding color. Each board has two doors of each color. The player can hold only one key at a time. The goal is to find a crown that’s locked away somewhere in the maze.

Figure 5-1. The game in action
In this version, data storage comes into play because the user can save a game that’s currently in progress and start again later from that point. All the saved game information is serialized and deserialized by the GameInfo class (see Listing 5-4). As you’ll see in the code, the GameInfo class stores the number of the board that the player is currently on, the current location of the player, the current locations of the keys, the key (if any) that’s currently in the player’s hand, the doors that are already open, and the time on the clock. All the locations are in terms of their coordinates on a 16x16 grid, so they’re integers from 0 to 15 (inclusive). Therefore, to save memory, I’ve stocked the coordinates two to a byte using the DataConverter class in Listing 5-3. The time on the clock, however, may be a very large number, so I use all 4 bytes to store it. Listing 5-4 shows the code for GameInfo.java.
Listing 5-4. GameInfo.java
package net.frog_parrot.dungeon;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.rms.*;
import net.frog_parrot.util.DataConverter;
/**
* This class contains the data for a game currently in progress.
* used to store a game and to resume a stored game.
*
* @author Carol Hamer
*/
public class GameInfo {
//----------------------------------------------------- // fields
/**
* The name of the datastore.
*/
public static final String STORE = "GameInfo";
/**
* This is set to true if an attempt is made to
* read agame when no game has been saved.
*/
private boolean myNoDataSaved;
/**
* The number that indicates which board the player
* is currently on.
*/
private int myBoardNum;
/**
* The amount of time that has passed.
*/
private int myTime;
/**
* The coordinates of where the player is on the board.
* coordinate values must be between 0 and 15.
*/
private int[] myPlayerSquare;
/**
* The coordinates of where the keys are currently found.
* MUST BE four sets of two integer coordinates.
* coordinate values must be between 0 and 15.
*/
private int[][] myKeyCoords;
/**
* The list of which doors are currently open.
* 0 = open
* 1 = closed
* WARNING: this array MUST have length 8.
*/
private int[] myDoorsOpen;
/**
* The number of the key that is currently being held
* by the player. if no key is held, then the value is -1.
*/
private int myHeldKey;
//------------------------------------------------------- // data gets/sets
/**
* @return true if no saved game records were found.
*/
boolean getIsEmpty() {
return(myNoDataSaved);
}
/**
* @return The number that indicates which board the player
* is currently on.
*/
int getBoardNum() {
return(myBoardNum);
}
/**
* @return The number of the key that is currently being held
* by the player. if no key is held, then the value is -1.
*/
int getHeldKey() {
return(myHeldKey);
}
/**
* @return The amount of time that has passed.
*/
int getTime() {
return(myTime);
}
/**
*@return The coordinates of where the player is on the board.
*coordinate values must be between 0 and 15.
*/
int[] getPlayerSquare() {
return(myPlayerSquare);
}
/**
*@return The coordinates of where the keys are currently found.
*MUST BE four sets of two integer coordinates.
*coordinate values must be between 0 and 15.
*/
int[][] getKeyCoords() {
return(myKeyCoords);
}
/**
*@return The list of which doors are currently open.
* 0 = open
* 1 = closed
* WARNING: this array MUST have length 8.
*/
int[] getDoorsOpen() {
return(myDoorsOpen);
}
//------------------------------------------------------- // constructors
/**
* This constructor records the game info of agame currently
* in progress.
*/
GameInfo(int boardNum, int time, int[] playerSquare, int[][] keyCoords,
int[] doorsOpen, int heldKey) throws Exception {
myBoardNum = boardNum;
myTime = time;
myPlayerSquare = playerSquare;
myKeyCoords = keyCoords;
myDoorsOpen = doorsOpen;
myHeldKey = heldKey;
encodeInfo();
}
/**
* This constructor reads the game configuration from memory.
* This is used to reconstruct a saved game.
*/
GameInfo() {
RecordStore store = null;
try {
// if the record store does not yet exist, don't
// create it
store = RecordStore.openRecordStore(STORE, false);
if((store != null) && (store.getNumRecords() > 0)) {
// the first record has id number 1
// it should also be the only record since this
// particular game stores only one game.
byte[] data = store.getRecord(1);
myBoardNum = data[0];
myPlayerSquare = DataConverter.decodeCoords(data[1]);
myKeyCoords = new int[4][];
myKeyCoords[0] = DataConverter.decodeCoords(data[2]);
myKeyCoords[1] = DataConverter.decodeCoords(data[3]);
myKeyCoords[2] = DataConverter.decodeCoords(data[4]);
myKeyCoords[3] = DataConverter.decodeCoords(data[5]);
myDoorsOpen = DataConverter.decode8(data[6]);
myHeldKey = data[7];
byte[] fourBytes = new byte[4];
System.arraycopy(data, 8, fourBytes, 0, 4);
myTime = DataConverter.parseInt(fourBytes);
} else {
myNoDataSaved = true;
}
} catch(Exception e) {
// this throws when the record store doesn't exist.
// for that or any error, you assume no data is saved: myNoDataSaved = true;
} finally {
try {
if(store != null) {
store.closeRecordStore();
}
} catch(Exception e) {
// if the record store is open this shouldn't throw.
}
}
}
//------------------------------------------------------- // encoding method
/**
* Turn the data into abyte array and save it.
*/
private void encodeInfo() throws Exception {
RecordStore store = null;
try {
byte[] data = new byte[12];
data[0] = (new Integer(myBoardNum)).byteValue();
data[1] = DataConverter.encodeCoords(myPlayerSquare);
data[2] = DataConverter.encodeCoords(myKeyCoords[0]);
data[3] = DataConverter.encodeCoords(myKeyCoords[1]);
data[4] = DataConverter.encodeCoords(myKeyCoords[2]);
data[5] = DataConverter.encodeCoords(myKeyCoords[3]);
data[6] = DataConverter.encode8(myDoorsOpen, 0);
data[7] = (new Integer(myHeldKey)).byteValue();
byte[] timeBytes = DataConverter.intToFourBytes(myTime);
System.arraycopy(timeBytes, 0, data, 8, 4);
// if the record store does not yet exist, the second
// arg "true" tells it to create.
store = RecordStore.openRecordStore(STORE, true);
int numRecords = store.getNumRecords();
if(numRecords > 0) {
store.setRecord(1, data, 0, data.length);
} else {
store.addRecord(data, 0, data.length);
}
} catch(Exception e) {
throw(e);
} finally {
try {
if(store != null) {
store.closeRecordStore();
}
} catch(Exception e) {
// if the record store is open this shouldn't throw.
}
}
}
}
In this version of the game, I’m not storing the floor plan of each board in memory records on the device. That’s because the dungeons themselves aren’t created by the user’s interaction with the game; they’re created in advance. In the next chapter, you’ll use the same basic game, but you’ll store the various boards in memory as the user downloads them from a game site. In this version, I’ve compacted all the information for the boards into bytes in anticipation of the next version in which the boards themselves will be downloaded and stored locally. Listing 5-5 shows the class that converts an array of bytes to a dungeon (BoardDecoder.java).
Next: Converting an array of bytes into a dungeon >>
More Java Articles
More By Apress Publishing
|
This article is taken from chapter five of the book J2ME Games with MIDP2, written by Carol Hamer (Apress, 2004; ISBN: 1590593820). Check it out at your favorite bookstore. Buy this book now.
|
|