用NetBeans开发平台开发J2ME游戏实例讲解(第四部分)
lirincy@163.com 林刚 2005.07 转载请注明出处作者(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));
}
}