这学期J2ME告一段落了,没学到什么东西,老师已经让交作业了。时间太紧写个扫雷凑数。
写了四个类:
MineGame: 处理游戏逻辑
MineBoard: 游戏的面板(canvas)
MineMIDlet: MIDlet主类,启动游戏,调度游戏面板
MineDefs: 没别的作用,就是定义一些常量,供其它类使用。
下面是MineDefs类,各常量的意思从字面就能看出来
//MineDefs: 定义常量
public class MineDefs
{
//雷方块状态
public static final byte STATE_FLAG_MINE =0;
public static final byte STATE_NORMAL_MINE =1;
public static final byte STATE_FLAG_EMPTY =2;
public static final byte STATE_NORMAL_EMPTY =3;
public static final byte STATE_OPEN_0 =8;
public static final byte STATE_OPEN_1 =9;
public static final byte STATE_OPEN_2 =10;
public static final byte STATE_OPEN_3 =11;
public static final byte STATE_OPEN_4 =12;
public static final byte STATE_OPEN_5 =13;
public static final byte STATE_OPEN_6 =14;
public static final byte STATE_OPEN_7 =15;
public static final byte STATE_OPEN_8 =16;
public static final byte STATE_OVER_BLAST =26;
public static final byte STATE_OVER_MINE =28;
//游戏状态
public static final byte GAMESTATE_RUN =1;
public static final byte GAMESTATE_OVER =2;
public static final byte GAMESTATE_WIN =3;
public static final byte HASOPENED =100;
public static final byte DONTEXPAND =101;
public static final byte WRONGMINENUM =102;
//格子数与雷数
public static final byte GRIDNUM_X =9;
public static final byte GRIDNUM_Y =9;
public static final byte MINENUM =10;
}
MineMIDlet主类,有两个按钮,退出和重新开始
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class MineMIDlet extends MIDlet implements CommandListener
{
Display display;
MineBoard gameCanvas;
MineGame game;
Command cmdExit=new Command("退出",Command.EXIT,0);
Command cmdRestart=new Command("重新开始",Command.OK,1);
public void startApp() throws MIDletStateChangeException
{
display = Display.getDisplay(this);
game=new MineGame();
gameCanvas=new MineBoard(game);
gameCanvas.addCommand(cmdExit);
gameCanvas.addCommand(cmdRestart);
gameCanvas.setCommandListener(this);
display.setCurrent(gameCanvas);
}
public void pauseApp(){ }
public void destroyApp(boolean unconditional){ notifyDestroyed(); }
public void commandAction(Command c, Displayable d)
{
if(c.equals(cmdExit))
{
destroyApp(false);
}
else
{
game.restart();
gameCanvas.repaint();
}
}
}
下面是最关键的两个类
import javax.microedition.lcdui.*;
MineBoard:游戏的面板 同时处理按键
public class MineBoard extends Canvas
{
private int scrWidth,scrHeight;
private int boardWidth,boardHeight; //雷区宽高
private int boardLeft,boardTop;
private int gridWidth; //格子的边长
private byte curPosX=0; //光标位置
private byte curPosY=0;
private String extraInfo="";
private MineGame game;
public MineBoard(MineGame game)
{ //在构造函数中计算布局
this.game=game;
scrWidth=getWidth();
scrHeight=getHeight();
int tmpgridWidth=scrWidth/MineDefs.GRIDNUM_X;
int tmpgridHeight=scrHeight/MineDefs.GRIDNUM_Y;
if(tmpgridWidth<tmpgridHeight)
{
gridWidth=tmpgridWidth;
boardWidth=gridWidth*MineDefs.GRIDNUM_X;
boardHeight=gridWidth*MineDefs.GRIDNUM_Y;
boardLeft=0;
boardTop=scrHeight-boardHeight;
}
else
{
gridWidth=tmpgridHeight;
boardWidth=gridWidth*MineDefs.GRIDNUM_X;
boardHeight=gridWidth*MineDefs.GRIDNUM_Y;
boardLeft=scrWidth-boardWidth;
boardTop=0;
}
}
public void paint(Graphics g)
{
g.setColor(150,150,255); //清屏
g.fillRect(0,0,scrWidth,scrHeight);
//画游戏状态
String state="";
switch(game.getGameState())
{
case MineDefs.GAMESTATE_OVER:
state="失败";
break;
case MineDefs.GAMESTATE_WIN:
state="胜利";
break;
}
if(scrWidth<scrHeight)
{
g.setColor(255,0,0);
g.drawString(state,0,0,Graphics.LEFT|Graphics.TOP);
g.drawString("还有"+game.getMinesRemain()+"颗雷",30,0,Graphics.LEFT|Graphics.TOP);
g.drawString(extraInfo,0,10,Graphics.LEFT|Graphics.TOP);
}
//画雷区
g.translate(boardLeft,boardTop);//调整起点
for(byte i=0;i<MineDefs.GRIDNUM_X;++i)
{
for(byte j=0;j<MineDefs.GRIDNUM_Y;++j)
{
drawGrid(i,j,game.getGridState(i,j),g);
}
}
//画光标
g.setColor(255,0,0);
g.drawRect(curPosX*gridWidth+1,curPosY*gridWidth+1,gridWidth-2,gridWidth-2);
}
//画每个雷方格的函数,如果需要可以修改这里让界面更好看
private void drawGrid(byte x,byte y,byte gridState,Graphics g)
{g.setColor(0,0,0);
switch(gridState)
{
case MineDefs.STATE_FLAG_MINE:
case MineDefs.STATE_FLAG_EMPTY://有标记
g.setColor(0,0,0);
g.fillRect(x*gridWidth,y*gridWidth,gridWidth,gridWidth);
break;
case MineDefs.STATE_NORMAL_MINE:
case MineDefs.STATE_NORMAL_EMPTY: //正常未打开
g.setColor(255,255,255);
g.fillRect(x*gridWidth,y*gridWidth,gridWidth,gridWidth);
break;
case MineDefs.STATE_OPEN_0:
//g.drawString("0",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
g.setColor(255,255,0);
g.fillRect(x*gridWidth,y*gridWidth,gridWidth,gridWidth);
break;
case MineDefs.STATE_OPEN_1:
g.drawString("1",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
case MineDefs.STATE_OPEN_2:
g.drawString("2",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
case MineDefs.STATE_OPEN_3:
g.drawString("3",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
case MineDefs.STATE_OPEN_4:
g.drawString("4",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
case MineDefs.STATE_OPEN_5:
g.drawString("5",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
case MineDefs.STATE_OPEN_6:
g.drawString("6",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
case MineDefs.STATE_OPEN_7:
g.drawString("7",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
case MineDefs.STATE_OPEN_8:
g.drawString("8",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
case MineDefs.STATE_OVER_BLAST:
g.drawString("B",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
case MineDefs.STATE_OVER_MINE:
g.drawString("M",x*gridWidth+7,y*gridWidth,Graphics.HCENTER|Graphics.TOP);
break;
}
}
protected void keyPressed(int key)
{//处理键盘事件
if(game.getGameState()!=MineDefs.GAMESTATE_RUN)
return;
extraInfo="";
switch ( getGameAction(key) )
{
case Canvas.UP:
if(curPosY>0)
{
--curPosY;
repaint();
}
break;
case Canvas.DOWN:
if(curPosY<MineDefs.GRIDNUM_Y-1)
{
++curPosY;
repaint();
}
break;
case Canvas.LEFT:
if(curPosX>0)
{
--curPosX;
repaint();
}
break;
case Canvas.RIGHT:
if(curPosX<MineDefs.GRIDNUM_X-1)
{
++curPosX;
repaint();
}
break;
case Canvas.GAME_A://数字1键(切换标记)
//System.out.println ("标记");
if(game.flagGrid(curPosX,curPosY)==MineDefs.WRONGMINENUM)
{
extraInfo="雷数目不正确";
}
repaint();
break;
case Canvas.GAME_B://数字3键(展开区域)
//System.out.println ("拓展");
if(game.expand(curPosX,curPosY)==MineDefs.DONTEXPAND)
{//不允许扩展
extraInfo="不允许扩展";
}
repaint();
break;
case Canvas.FIRE: //打开格子
//System.out.println ("打开");
if(game.hitGrid(curPosX,curPosY)==MineDefs.HASOPENED)
{
extraInfo="已经打开";
}
repaint();
break;
}
}
}
MineGame:游戏的核心逻辑
import java.util.*;
public class MineGame
{
//表示雷区的二维数组
private byte mines[][]=new byte[MineDefs.GRIDNUM_X][MineDefs.GRIDNUM_Y];
private byte gameState;
private byte minesRemain=0;
public MineGame()
{
restart();
}
//游戏重新开始
public void restart()
{//System.out.println ("重新开始");
minesRemain=0;
clearMines();
layMines();
gameState=MineDefs.GAMESTATE_RUN;
}
//清空雷区
private void clearMines()
{
for(byte i=0;i<MineDefs.GRIDNUM_X;++i)
{
for(byte j=0;j<MineDefs.GRIDNUM_Y;++j)
{
mines[i][j]=MineDefs.STATE_NORMAL_EMPTY;
}
}
}
//随即布置雷
private void layMines()
{
Random random = new Random(Calendar.getInstance().getTime().getTime());
int x,y;
for(byte i=0;i<MineDefs.MINENUM;)
{
x = Math.abs(random.nextInt()) % MineDefs.GRIDNUM_X;
y = Math.abs(random.nextInt()) % MineDefs.GRIDNUM_Y;
if(mines[x][y]>MineDefs.STATE_NORMAL_MINE)//后面的状态均表示无雷
{
mines[x][y]=MineDefs.STATE_NORMAL_MINE;
++minesRemain;
++i;
}
}
//dis();
}
//
public void dis()
//
{
//
//输出雷区状态
//
for(byte i=0;i<MineDefs.GRIDNUM_Y;++i)
//
{
//
for(byte j=0;j<MineDefs.GRIDNUM_X;++j)
//
{
//
System.out.print (mines[j][i]+" ");
//
}
//
System.out.println ();
//
}
//
}
//获取方块的状态
public byte getGridState(byte x,byte y)
{
return mines[x][y];
}
public byte getGameState()
{
return gameState;
}
public byte getMinesRemain()
{
return minesRemain;
}
//计算周围有多少雷
private byte getAroundMine(byte x,byte y)
{
byte num=0;
byte minX=(byte)((x==0)? 0:x-1);
byte maxX=(byte)((x==MineDefs.GRIDNUM_X-1)? MineDefs.GRIDNUM_X-1:x+1);
byte minY=(byte)((y==0)? 0:y-1);
byte maxY=(byte)((y==MineDefs.GRIDNUM_Y-1)? MineDefs.GRIDNUM_Y-1:y+1);
for(int i=minX;i<=maxX;++i)
{
for(int j=minY;j<=maxY;++j)
{
if(mines[i][j]<=MineDefs.STATE_NORMAL_MINE)//此前状态均为有雷
++num;
}
}
return num;
}
//计算周围作了几个标记
private byte getAroundFlag(byte x,byte y)
{
byte num=0;
byte minX=(byte)((x==0)? 0:x-1);
byte maxX=(byte)((x==MineDefs.GRIDNUM_X-1)? MineDefs.GRIDNUM_X-1:x+1);
byte minY=(byte)((y==0)? 0:y-1);
byte maxY=(byte)((y==MineDefs.GRIDNUM_Y-1)? MineDefs.GRIDNUM_Y-1:y+1);
for(int i=minX;i<=maxX;++i)
{
for(int j=minY;j<=maxY;++j)
{
if(mines[i][j]==MineDefs.STATE_FLAG_MINE||mines[i][j]==MineDefs.STATE_FLAG_EMPTY)//有标记的
++num;
}
}
return num;
}
//打开一个雷方块
public byte hitGrid(byte x,byte y) //返回游戏状态
{//System.out.println (x+" "+y+" "+mines[x][y]);
if(mines[x][y]>=MineDefs.STATE_OPEN_0)//此处已经打开
{//System.out.println (x+" "+y+" "+mines[x][y]+" "+MineDefs.STATE_OPEN_0+"此处已经打开");
return MineDefs.HASOPENED;
}
if(mines[x][y]<=MineDefs.STATE_NORMAL_MINE)//点中了地雷
{//System.out.println (x+" "+y+" "+mines[x][y]+" "+MineDefs.STATE_NORMAL_MINE+"点中了地雷");
gameState=MineDefs.GAMESTATE_OVER;
for(byte i=0;i<MineDefs.GRIDNUM_X;++i)
{
for(byte j=0;j<MineDefs.GRIDNUM_Y;++j)
{
if(mines[i][j]<=MineDefs.STATE_NORMAL_MINE)//此前状态均为有雷
mines[i][j]=MineDefs.STATE_OVER_MINE;
}
}
mines[x][y]=MineDefs.STATE_OVER_BLAST;
}
else //未点中地雷,改变相应状态
{//System.out.println (x+" "+y+" "+mines[x][y]+" "+"未点中地雷");
byte aroundNum=getAroundMine(x,y);//System.out.println (x+" "+y+" "+mines[x][y]+"周围有"+aroundNum);
mines[x][y]=(byte)(MineDefs.STATE_OPEN_0+aroundNum);//System.out.println (x+" "+y+" "+mines[x][y]);
if(aroundNum==0) //周围没有雷 拓展
{//System.out.println (x+" "+y+" "+mines[x][y]+"拓展");
expand(x,y);
}
}
return gameState;
}
//标记一个雷方块
public byte flagGrid(byte x,byte y)
{
if(mines[x][y]>=MineDefs.STATE_OPEN_0)//此处已经打开
return MineDefs.HASOPENED;
if(mines[x][y]==MineDefs.STATE_FLAG_MINE||mines[x][y]==MineDefs.STATE_FLAG_EMPTY)//原有标记
{
++mines[x][y]; //转换到对应的无标记状态
++minesRemain;//System.out.println ("还有雷"+minesRemain);
if(isVictory())
{
gameState=MineDefs.GAMESTATE_WIN;
}
}
else //原无标记
{
if(minesRemain<=0)
return MineDefs.WRONGMINENUM;
--mines[x][y];
--minesRemain;//System.out.println ("还有雷"+minesRemain);
if(isVictory())
{
gameState=MineDefs.GAMESTATE_WIN;
}
}
return gameState;
}
//拓展方块(如果此方块周围没有类,或雷已标记)
public byte expand(byte x,byte y)
{
byte aroundNum=getAroundMine(x,y);
if(aroundNum!=getAroundFlag(x,y))//没有标完雷不允许展开
return MineDefs.DONTEXPAND;
byte minX=(byte)((x==0)? 0:x-1);
byte maxX=(byte)((x==MineDefs.GRIDNUM_X-1)? MineDefs.GRIDNUM_X-1:x+1);
byte minY=(byte)((y==0)? 0:y-1);
byte maxY=(byte)((y==MineDefs.GRIDNUM_Y-1)? MineDefs.GRIDNUM_Y-1:y+1);
for(byte i=minX;i<=maxX;++i)
{
for(byte j=minY;j<=maxY;++j)
{
if(mines[i][j]==MineDefs.STATE_NORMAL_MINE||mines[i][j]==MineDefs.STATE_NORMAL_EMPTY)
hitGrid(i,j);
}
}
return gameState;
}
//判断游戏是否胜利
private boolean isVictory()
{
if(minesRemain>0)
{
return false;
}//System.out.println ("标完");
for(byte i=0;i<MineDefs.GRIDNUM_X;++i)
{
for(byte j=0;j<MineDefs.GRIDNUM_Y;++j)
{
if(mines[i][j]==MineDefs.STATE_FLAG_EMPTY)//此前状态均为有雷
return false;
}
}
return true;
}
}
这个扫雷具有最基本的游戏功能,如果需要,可以方便的添加其它附加功能,比如使用RMS保存游戏记录,或者加上声音。