天天看点

JAVA游戏编程之二----j2me MIDlet 手机游戏入门开发--扫雷(2)-不含线程

作者:雷神

QQ:38929568

QQ群:28048051JAVA游戏编程(满) 28047782(将满)

扫雷(2)是在扫雷(1)的基础上增加 完善了部分代码基本逻辑不变!

使得游戏更好玩了,代码两也增加到400行,比较适合初学者,可读性强,有详尽的代码注释。

数字键1标红旗,不确定,取消标记。数字键3显示/不显示 游戏信息!

方向键,数字键2468,控制光标上下左右移动!

程序运行如图

<a target="_blank" href="http://blog.51cto.com/attachment/201105/162216937.jpg"></a>

Minesweeper.java 

这个类是游戏入口类,基本不做修改!而且每个游戏的这个midlet类都差不太多!

///////////////////////////////////////////////////////////////////////////////////////////////////  

//  

// Minesweeper.java  

// Project: Minesweeper  

// Author(s): Gao Lei  

// Create: 2007-10-08  

import javax.microedition.midlet.*;    //j2me MIDlet程序必须继承MIDlet类,所以要引入此包  

import javax.microedition.lcdui.*;    //Display这个类所在包  

public class Minesweeper extends MIDlet   

...{  

    static Minesweeper s_midlet;    //MIDlet类的静态对象,方便实用 MIDlet类方法  

    static Display s_display = null;//用来显示 Canvas  

    static cGame s_game = null;        //Canvas类对象,主要实现游戏的类  

    public Minesweeper()  

    ...{  

        s_midlet = this;  

    }  

    /** *//**  

     * 程序开始 系统会调用这个函数  

     * 也有些手机 可以把程序初始化部分放到构造函数里,这连个地方应视手机的不同而定!  

     */ 

    public void startApp()               

        if (s_display == null)   

        ...{  

            s_display = Display.getDisplay(this);//创建Display对象,参数是MIDlet类对象,也就是我们当前写的这个Minesweeper类  

        }  

        if (s_game == null)   

            s_game = new cGame();                //创建 Canvas对象  

            s_display.setCurrent(s_game);        //把Canvas对象设置成当前显示  

        }   

        else   

            s_display.setCurrent(s_game);  

     * 程序暂停 系统会自动调用这个函数,不是所有手机都支持,  

     * 手机在接到中断,如 来电,来短信时候会调用这个函数,这个函数 通常是空的!  

    public void pauseApp()           

     * 程序关闭 系统会调用这个函数,如果希望关闭程序的时候保存数据,可在这个函数里添加保存数据的方法  

     * 比如游戏进行中,按了关机键,程序就会调用这个函数,也可以在程序中调用这个函数来结束游戏!  

    public void destroyApp(boolean unconditional)   

        notifyDestroyed();  

cGame.java 这个类添加了几个方法,主要是绘制图形,和完善游戏,增加了游戏胜利和游戏难度。

////////////////////////////////////////////////////////////////////////////////  

// cGame.java  

import java.util.Random;            //得到 随机函数  

import javax.microedition.lcdui.*;    //写界面所需要的包  

class cGame extends Canvas   

    //游戏状态  

    private static final int STATEPLAY        = 0;    //游戏中  

    private static final int STATELOST        = 1;    //游戏失败  

    private static final int STATEWIN        = 2;    //游戏胜利  

    //格子状态  

    private static final int MINE_OFF_SHOW    = 0;    //不显示格子中雷的数  

    private static final int MINE_ON_SHOW    = 1;    //显示格子中雷的数  

    private static final int MINE_ASK        = 9;    //设置问号  

    private static final int MINE_FLAG        = 10;    //设置红旗  

    private static final int MINE_GUESS_ERR    = 11;    //显示猜错了雷  

    //定义键值  

    private static final int KEY_UP         = 1;    //上  

    private static final int KEY_DOWN         = 2;    //下  

    private static final int KEY_LEFT         = 3;    //左  

    private static final int KEY_RIGHT         = 4;    //右  

    private static final int KEY_FIRE         = 5;    //中间确认键  

    public static Random rand;            //随机数对象  

    private int map_x     = 10;            //雷区的 行数        // 15  

    private int map_y     = 10;            //雷区的 列数        // 12  

    private int map_w     = 20;            //一个雷区的格子的宽度  

    private int map_h     = 20;            //一个雷区的格子的高度  

    private int key_x     = map_x / 2;    //游戏初始时 光标所在雷区的格子位置  

    private int key_y     = map_y / 2;    //游戏初始时 光标所在雷区的格子位置  

    private int mine_num= 10;            //雷区的雷数 不应该大于雷区的格子总数  

    private int flagNum    = mine_num;        //剩余红旗数   

    private int rightNum= 0;            //猜对的雷数  

    private int[][] map;                //雷区的地图数组 &gt;=10 为雷, &lt;10 为周围的雷数, 0位附近没有雷  

    private int[][] map_show;            //雷区的地图数组是否显示该位置的雷数//1显示//0不显示//9问号//10红旗  

    private int     gameState     = STATEPLAY;    //游戏状态  

    private int     s_width     = 0;            //屏幕尺寸 宽  

    private int     s_height    = 0;            //屏幕尺寸 高  

    private int     addMine        = 0;            //重新开始后雷数增加的个数  

    private boolean isShowInfo    = false;        //是否显示游戏信息  

    private String     strFlagNum     = "红旗数";  

    private String     strMineNum     = "正确率";  

    private String[] gameInfo     = ...{"游戏中","游戏失败 按0重新开始","游戏胜利 按0进入多雷区"};  

    private Font     font         = Font.getFont( Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE );  

    cGame()  

        setFullScreenMode(true);        //设置游戏为全屏幕模式,该函数只能在支持midp2.0的手机上使用  

        s_width = getWidth();            //得到屏幕尺寸     宽  

        s_height= getHeight();            //得到屏幕尺寸     高  

        rePlay( 0 );                    //游戏初始化//重新游戏  

     * 系统自动调用该绘图函数,并传入绘图设备g,通过该设备,我们可以绘制如直线,矩形快,字符串,图片等,  

    public void paint(Graphics g)  

        g.setClip(0, 0, s_width, s_height);        //设置参数描述的区域为操作区  

        g.setColor(0x000000);                    //设置颜色为 黑色, 三个16进制数表示,RGB,如0x00ff00 为绿色  

        g.fillRect(0, 0, s_width, s_height);    //绘制一个实心矩形区域  

        g.setColor(0xFFFFFF);                    //设置颜色为 白色  

        //绘制雷区  

        for( int i=0; i&lt;=map_y; i++ )    // |||    //画 map_y+1条竖线  

            g.drawLine(i*map_w, 0, i*map_w, map_h*map_x);  

        for( int i=0; i&lt;=map_x; i++ )     // ===    //画 map_x+1条横线  

            g.drawLine(0, i*map_h, map_y*map_w, i*map_h);  

        for( int i=0; i&lt;map_x; i++ )              

            for( int j=0; j&lt;map_y; j++ )  

            ...{  

                if( map_show[i][j] == MINE_ON_SHOW )    //遍历地图数组 看该位置的雷数 是否应该显示  

                ...{  

                    if( map[i][j]==0 )            //周围没有雷  

                    ...{  

                        g.setColor(0x666666);  

                        g.fillRect(j*map_h+2, i*map_w+2, map_w-3, map_h-3);  

                    }  

                    else if(map[i][j]&lt;10)        //显示周围的雷数  

                        g.setColor(0x00ff00);  

                        g.drawString(""+map[i][j], j*map_h+8, i*map_w+4, g.LEFT|g.TOP);    //显示该位置的雷数  

                    else                        //踩到雷了  

                        g.setColor(0xff0000);  

//                        g.drawString(""+map[i][j], j*map_h+5, i*map_w+4, g.LEFT|g.TOP);    //显示该位置是雷  

                }  

                else if( map_show[i][j] == MINE_FLAG )        //显示红旗  

                    paintFlag( g, j, i );  

                else if( map_show[i][j] == MINE_ASK )        //显示问号  

                    paintInterrogation( g, j, i );  

                else if( map_show[i][j] == MINE_GUESS_ERR )//显示猜错了  

                    g.setColor(0x666666);  

                    g.fillRect(j*map_h+2, i*map_w+2, map_w-3, map_h-3);  

                    paintGuessErr( g, j, i );  

            }      

        g.setColor(0xFF0000);                    //设置颜色 红  

        g.drawRect(key_x*map_w+1, key_y*map_h+1, map_w-2, map_h-2);        //绘制一个空心矩形框//为光标  

        g.drawRect(key_x*map_w+2, key_y*map_h+2, map_w-4, map_h-4);        //绘制一个空心矩形框//为光标  

        if( isShowInfo || gameState != STATEPLAY )    //如果游戏 结束  

            g.setFont( font );  

            g.drawString( strFlagNum+":"+flagNum,     20, s_height-60, g.LEFT|g.TOP );    //显示剩余旗数  

            g.drawString( strMineNum+":"+rightNum +"/"+ mine_num,    20, s_height-45, g.LEFT|g.TOP );    //显示正确率 猜对雷数/总雷数  

            g.drawString( gameInfo[ gameState ],     20, s_height-30, g.LEFT|g.TOP );    //显示游戏状态  

     * 系统自动调用该函数,当有键盘事件发生为按下某键,参数key为按下键的键值  

    public void keyPressed(int key)  

        key = Math.abs(key);  

        System.out.println("key="+key);  

        //上下左右 为移动光标事件,只需要调整光标位置即可,但需要做边界判断  

        switch( key )  

            case KEY_NUM2:  

            case KEY_UP:  

                if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了      

                    break;  

                else 

                    key_y--;  

                    if( key_y&lt;0 )  

                        key_y = map_x-1;  

            break;  

            case KEY_NUM8:  

            case KEY_DOWN:  

                    key_y++;  

                    key_y %=map_x;  

            case KEY_NUM4:  

            case KEY_LEFT:  

                    key_x--;  

                    if( key_x&lt;0 )      

                        key_x = map_y-1;  

            case KEY_NUM6:  

            case KEY_RIGHT:  

                    key_x++;  

                    key_x %=map_y;  

            case KEY_FIRE:  

            case KEY_NUM5:  

                if( gameState == STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了      

                    if( map_show[key_y][key_x] == MINE_FLAG )  

                        break;  

                    showMap( key_y, key_x );    //显示该位置的雷数  

                    if( map[key_y][key_x] &gt;=10 )//如果雷数&gt;=10 该位置是雷,  

                        isWinGame();  

                        addMine        = 0;  

                        isShowInfo     = true;  

                        gameState = STATELOST;    //游戏失败  

            case KEY_NUM1:                        //设置红旗//问号//取消  

                switch( map_show[key_y][key_x] )  

                case MINE_OFF_SHOW:  

                    map_show[key_y][key_x] = MINE_FLAG;  

                    flagNum--;  

                    if( flagNum == 0 )  

                        if( isWinGame() )  

                        ...{  

                            addMine        = 5;  

                            isShowInfo     = true;  

                            gameState      = STATEWIN;  

                        }  

                        else 

                            addMine        = 0;  

                            gameState      = STATELOST;  

                break;  

                case MINE_FLAG:  

                    flagNum++;  

                    map_show[key_y][key_x] = MINE_ASK;  

                case MINE_ASK:  

                    map_show[key_y][key_x] = MINE_OFF_SHOW;  

            case KEY_NUM3:                        //是否显示游戏信息  

                isShowInfo = !isShowInfo;  

            case KEY_NUM0:                        //当按下 数字键 0  

                rePlay( addMine );                    //重新开始游戏      

        /** *//**  

         * 重新执行 paint() 但该函数是立刻返回,也就是说他不会等待paint()执行完毕就返回了,  

         * 如果 需要 paint()执行完毕才返回,可以使用serviceRepaints(),也可以两个都是用,但  

         * repaint()应该在serviceRepaints()之前.  

         */ 

        this.repaint();                        //本例为键盘事件驱动,刷新函数也就是每次按下键盘才作  

    boolean isWinGame()  

        boolean isWin = true;  

                if( map_show[i][j] == MINE_FLAG )    //显示红旗  

                    if( map[i][j] &lt; 10 )            //地雷猜错了  

                        map_show[i][j] = MINE_GUESS_ERR;  

                        isWin = false;  

                    else 

                        rightNum ++;  

            }  

        return isWin;                                //群不红旗都插对了 才能通关  

    void paintFlag( Graphics g, int key_x, int key_y )  

        int x = key_x*map_h+9;  

        int y = key_y*map_w+5;  

        g.setColor( 0xFF0000 );  

        g.drawLine( x  , y   , x  , y+11 );    //    |  

        g.drawLine( x-2, y+11, x+3, y+11 );    //    -  

        g.drawLine( x  , y   , x+5, y+ 2 );    //    &gt;  

        g.drawLine( x+5, y+2 , x  , y+ 4 );    //    &gt;  

        x += 1;  

        y += 1;  

        g.drawLine( x-5, y+11, x+4, y+11 );    //    -  

    void paintInterrogation( Graphics g, int key_x, int key_y )  

        int x = key_x*map_h+8;  

        g.drawString("?", x, y, g.LEFT|g.TOP);    //    ?  

    void paintGuessErr( Graphics g, int key_x, int key_y )  

        g.drawString("x", x, y, g.LEFT|g.TOP);    //    x  

    //该函数是一个递归函数,把当前位置设置成显示,并判断当前位置雷数是否为0个  

    //如果是0个雷,那么它周围的8个格子都要再作一次showMap  

    void showMap(int x, int y)  

        if( map_show[x][y] == MINE_FLAG )      

            return;  

        if( map_show[x][y] == MINE_ON_SHOW )  

        else 

            map_show[x][y] = MINE_ON_SHOW;  

        if( map[x][y] == 0 )  

            if( x-1 &gt;= 0 )  

                showMap( x-1, y );  

                if( y-1 &gt;= 0)        showMap( x-1, y-1 );  

                if( y+1 &lt; map_y)    showMap( x-1, y+1 );  

            if( y-1 &gt;= 0)            showMap( x  , y-1 );  

            if( y+1 &lt; map_y)        showMap( x  , y+1 );  

            if( x+1 &lt; map_x )  

                showMap( x+1, y );  

                if( y-1 &gt;= 0)        showMap( x+1, y-1 );  

                if( y+1 &lt; map_y)    showMap( x+1, y+1 );  

    //重新 开始 游戏  

    public void rePlay( int add )  

        if( add &gt; 0 )  

            map_x     += 1;                    //雷区的 行数    // 10 - 15  

            map_y     += 1;                    //雷区的 列数    // 10 - 12  

            if( map_x &gt;= 15 )    map_x = 15;  

            if( map_y &gt;= 12 )    map_y = 12;  

            addMine = 0;                    //每次增加的雷数  

            add = 0;  

        key_x         = map_x / 2;            //游戏初始时    光标所在雷区的格子位置  

        key_y         = map_y / 2;            //游戏初始时    光标所在雷区的格子位置  

        mine_num    += add;                    //雷区的雷数    不应该大于雷区的格子总数  

        if(mine_num &gt;= map_x*map_y/2)  

            mine_num = map_x*map_y/10;    //纠正雷数不能超过雷区的格子数  

        flagNum        = mine_num;            //剩余红旗数  

        rightNum    = 0;                //猜对的雷数  

        gameState    = STATEPLAY;  

        map          = new int[map_x][map_y];  

        map_show     = new int[map_x][map_y];  

        rand        = new Random( System.currentTimeMillis() );    //用事件作随机数种子的随机数  

        isShowInfo    = false;  

        //布雷  

        for(int i=0; i&lt;mine_num; i++)        //随机mine_num个雷的位置  

            int x = rand.nextInt( map_x );    //得到 随机数 雷格子的x方向位置  

            int y = rand.nextInt( map_y );    //得到 随机数 雷格子的y方向位置  

            if( map[x][y] &gt;= 10)            //如果该位置已经是雷了,就要重新布雷  

            ...{      

                i--;  

                continue;  

            map[x][y] = 10;                    //否则 将该位置 设定为雷  

            //并在该雷的周围 的雷数都作+1操作  

            //以下判断为 边角判断,防止访问数组越界  

            if( x-1 &gt;= 0 )                      

                map[x-1][y  ] += 1;  

                if( y-1 &gt;= 0)        map[x-1][y-1] += 1;  

                if( y+1 &lt; map_y)    map[x-1][y+1] += 1;  

            if( y-1 &gt;= 0)        map[x  ][y-1] += 1;  

            if( y+1 &lt; map_y)    map[x  ][y+1] += 1;  

                map[x+1][y  ] += 1;  

                if( y-1 &gt;= 0)        map[x+1][y-1] += 1;  

                if( y+1 &lt; map_y)    map[x+1][y+1] += 1;  

<a href="http://down.51cto.com/data/2358277" target="_blank">附件:http://down.51cto.com/data/2358277</a>

本文转自 kome2000 51CTO博客,原文链接:http://blog.51cto.com/kome2000/578481