(3) 使用rms來記錄成績排行榜
J2ME 記錄管理系統(RMS)提供了一種機制,通過這種機制,MIDlet 能夠持久存儲數據,並在以後檢索數據。我們在程序中就需要這樣的一個排行榜,
這個排行榜在程序關閉以後仍然能夠記錄數據,rms正好滿足了這個要求,有關rms的信息,請參考:
http://www-128.ibm.com/developerworks/cn/java/j-wi-rms/index.html
http://game.kongzhong.com/content/j2mecontent_730.html
我們在程序中要實現排行榜的功能,需要做的事情有這樣幾件:
A. 在選項菜單當中加入排行榜,這好辦, 在HuaRongDaoMielet.java中加入產生排行榜的代碼;
B. 新建一個類,取名Score, 關於新建類的方法,上面的論述中有講述,我們需要的接口函數很簡單,getScore(), setScore()就可以了,注意這
裏,rms是字節數組組成的記錄體系,因此我們如果要存儲整型量,就要有整形到byte[]以及byte[]到整型量的轉換,如下:
/**
* 數組到整數.
*/
private int getInt(byte[] buf, int offset) {
return (buf[offset+0] & 0xff) << 24 |
(buf[offset+1] & 0xff) << 16 |
(buf[offset+2] & 0xff) << 8 |
(buf[offset+3] & 0xff);
}
/**
* 整數到數組.
*/
private void putInt(byte[] buf, int offset, int value) {
buf[offset+0] = (byte)((value >> 24) & 0xff);
buf[offset+1] = (byte)((value >> 16) & 0xff);
buf[offset+2] = (byte)((value >> 8) & 0xff);
buf[offset+3] = (byte)((value >> 0) & 0xff);
}
其中offset是byte[]的偏移量,一個整型量用4個字節來表示。
另外我們需要對rms存儲系統進行初始化,在構造函數當中引入open()函數,如下所示:
// 每關成績 = {int level,int moves;}
private byte[] scoreRec; // 當前關成績.
private static final int SCORE_LEN = 8;
private RecordStore store; // 記錄存儲,沒有打開時爲空.
/*
* 構造函數.
*/
Score() {
store = null;
scoreRec = new byte[SCORE_LEN];
putInt(scoreRec, 0, 0);
putInt(scoreRec, 4, 0);
open();
}
/**
* 打開存儲記錄
*/
boolean open() {
try {
store = RecordStore.openRecordStore("HuaRongDaoScore", true);
} catch (RecordStoreException ex) {
}
if (store == null)
return false;
return true;
}
可以看出,我們的每個記錄長度是8個字節,都是整型量,第一個是關,第二個是成績,最後的結構類似於:
關 步數
0 33
1 98
2 109
3 77
...
最後,在這個類中最重要的兩個函數readScore()和setScore(), 分別負責讀取和設置某一關的成績。在這兩個函數當中,我們要注意在rms
中遍歷某個元素的方法,你需要自己設個標記TAG,來標記你的記錄,因爲rms不象數組一樣,可以按照下標來索引,rms的幾個函數包括
getRecord()
getRecordSize()
enumerateRecords()
addRecord()
setRecord()
等等都要知道是做什麼用的。
/**
* 讀取某級別的成績.
*/
public int readScore(int level) {
try {
// 查找成績
RecordEnumeration enm = store.enumerateRecords(null, null, false);
while (enm.hasNextElement()) {
int ndx = enm.nextRecordId();
if (store.getRecordSize(ndx) == SCORE_LEN) {
int l = store.getRecord(ndx, scoreRec, 0);
if (l == SCORE_LEN && getInt(scoreRec, 0) == level)
return getInt(scoreRec, 4);
}
}
// 沒有這一關的成績,新建
putInt(scoreRec, 0, level);
putInt(scoreRec, 4, 0);
store.addRecord(scoreRec, 0, SCORE_LEN);
return 0;
} catch (RecordStoreException ex) {
ex.printStackTrace();
return -1;
}
}
/**
* 設置某級別的成績.
*/
boolean setScore(int level, int moves) {
try {
// 查找成績
RecordEnumeration enm = store.enumerateRecords(null, null, false);
while (enm.hasNextElement()) {
int ndx = enm.nextRecordId();
if (store.getRecordSize(ndx) == SCORE_LEN) {
int l = store.getRecord(ndx, scoreRec, 0);
if (l == SCORE_LEN && getInt(scoreRec, 0) == level)
//只存儲較小的值
if ( getInt(scoreRec,4) == 0 || getInt(scoreRec, 4) > moves){
putInt(scoreRec, 4, moves);
store.setRecord(ndx, scoreRec, 0, SCORE_LEN);
}
}
}
// 沒有這一關的成績,新建
putInt(scoreRec, 0, level);
putInt(scoreRec, 4, moves);
store.addRecord(scoreRec, 0, SCORE_LEN);
} catch (RecordStoreException ex) {
ex.printStackTrace();
return false;
}
return true;
}
C. 最後要看看這個Score類怎樣使用,我們需要在主Midlet裏面初始化它,然後在ConrolLogic.java中 設置成績。
首先,聲明這個類成員, 並且進行初始化:HuaRongDaoMidlet.java
public class HuaRongDaoMidlet extends MIDlet implements CommandListener{
... ...
private Score score;
/** The Form object for the Options command */
private Form optionsForm;
... ...
public void startApp() {
score = new Score();
... ...
在程序結束時關閉它,
... ...
public void commandAction(Command c, Displayable d) {
if ( c == CMD_EXIT && d == splash ){
//退出
score.close();
destroyApp(false);
notifyDestroyed();
... ...
然後我們希望控制邏輯ControlLogic能夠使用這個Score的實例,因此我們把它作爲參數傳遞給ControlLogic.
... ...
}else if (c == CMD_START && d == splash) {
// 開始新遊戲
logic=new ControlLogic(levelChoice.getSelectedIndex()+1, score);
display.setCurrent(logic);
logic.addCommand(CMD_UNDO);
logic.addCommand(CMD_EXIT);
logic.setCommandListener(this);
... ...
最後,在ControlLogic.java當中我們可以開始使用Score類了:
先聲明 成員:
... ...
public int moves=0;//所用的步數.
private Score score;//排行榜
private History history = new History();
... ...
在初始化時接受參數:
... ...
public ControlLogic(int gameLevel, Score s) {//構造函數
try {
this.level = gameLevel;
score = s;
if ( score == null)
score.open();
... ...
最終我們在成績被刷新的地方設置成績。由於我們在Score類的setScore()函數中已經作了刷新成績判斷,這裏的調用就很簡單。
... ...
protected void paint(Graphics g) {
... ...
//顯示步數
Draw.paint(g,String.valueOf(moves), 60, 15, Graphics.BASELINE | Graphics.HCENTER);
if ( win()) {
isWin=true;
Draw.paint(g,"你贏了!", 60, 70, Graphics.TOP | Graphics.HCENTER);
score.setScore(level-1, moves);
}
因爲我們的關數是1,2,3,4...而rms存儲的關數是0,1,2...因此這裏有一個 -1的操作。
好了,至此我們的添加排行榜的工程就完成了,下面是HuaRongDaoMidlet.java至此的清單, 調試一下你的程序,看是不是專業了很多呢?
package HuaRongDao;
/*
* HuaRongDaoMidlet.java
*
* Created on 2005年7月1日, 下午8:18
*/
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.io.IOException;
import java.io.InputStream;
import java.lang.InterruptedException;
/**
*
* @author lin
* @version
*/
public class HuaRongDaoMidlet extends MIDlet implements CommandListener{
private Display display;
private SplashScreen splash;
private ControlLogic logic;
private Score score;
/** The Form object for the Options command */
private Form optionsForm;
/** Set of choices for the levels */
private ChoiceGroup levelChoice;
/** Set of lists for the topScores */
private String topScores;
private final static Command CMD_EXIT = new Command("退出", Command.EXIT, 1);
private final static Command CMD_OK = new Command("確認", Command.OK, 1);
private final static Command CMD_OPTION = new Command("選項", Command.SCREEN, 1);
private final static Command CMD_START = new Command("開始", Command.SCREEN, 1);
private final static Command CMD_PAUSE = new Command("暫停", Command.SCREEN, 1);
private final static Command CMD_UNDO = new Command("上一步", Command.SCREEN, 1);
public HuaRongDaoMidlet(){
display = Display.getDisplay(this);
}
public void startApp() {
score = new Score();
splash=new SplashScreen();
splash.addCommand(CMD_START);
splash.addCommand(CMD_EXIT);
splash.addCommand(CMD_OPTION);
splash.setCommandListener(this);
display.setCurrent(splash);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public void commandAction(Command c, Displayable d) {
if ( c == CMD_EXIT && d == splash ){
//退出
score.close();
destroyApp(false);
notifyDestroyed();
}else if (c == CMD_EXIT && d == logic){
//從遊戲中返回
//if (logic.isWin)
logic = null;
display.setCurrent(splash);
}else if (c == CMD_OPTION){
//進入選項
updateOptions();
display.setCurrent(genOptions());
}else if (c == CMD_OK && d == optionsForm) {
// 從選項回到主界面
display.setCurrent(splash);
}else if (c == CMD_START && d == splash) {
// 開始新遊戲
logic=new ControlLogic(levelChoice.getSelectedIndex()+1, score);
display.setCurrent(logic);
logic.addCommand(CMD_UNDO);
logic.addCommand(CMD_EXIT);
logic.setCommandListener(this);
}else if (c == CMD_PAUSE) {//處理“暫停”
//logic.pause();
}else if (c == CMD_UNDO) {//處理“上一步”
logic.unMove();
}
}
private Screen genOptions() {
if(optionsForm == null){
optionsForm = new Form("選項");
optionsForm.addCommand(CMD_OK);
optionsForm.setCommandListener(this);
levelChoice = new ChoiceGroup("華容道佈局", Choice.EXCLUSIVE);
levelChoice.append("過五關", null);
levelChoice.append("橫刀立馬", null);
levelChoice.append("水泄不通", null);
levelChoice.append("小燕出巢", null);
levelChoice.append("近在咫尺", null);
levelChoice.append("走投無路", null);
optionsForm.append(levelChoice);
updateOptions();
optionsForm.append(topScores);
optionsForm.append("小毛驢工作室,2005, 版權沒有,違者打屁股");
}
return optionsForm;
}
private void updateOptions() {
topScores = "積分榜/n";
if ( score.readScore(0)!= -1 )
topScores += "過五關最少步數: "+String.valueOf(score.readScore(0))+"/n";
if ( score.readScore(1)!= -1 )
topScores += "橫刀立馬最少步數:"+String.valueOf(score.readScore(1))+"/n";
if ( score.readScore(2)!= -1 )
topScores += "水泄不通最少步數:"+String.valueOf(score.readScore(2))+"/n";
if ( score.readScore(3)!= -1 )
topScores += "小燕出巢最少步數:"+String.valueOf(score.readScore(3))+"/n";
if ( score.readScore(4)!= -1 )
topScores += "近在咫尺最少步數:"+String.valueOf(score.readScore(4))+"/n";
if ( score.readScore(5)!= -1 )
topScores += "走投無路最少步數:"+String.valueOf(score.readScore(5));
}
}