JAVA游戏编程之二----j2me MIDlet 手机游戏入门开发--扫雷(3)-带线程--仿windows扫雷

 JAVA游戏编程之二----j2me MIDlet 手机游戏入门开发--扫雷(3)-带线程--仿windows扫雷

作者:雷神
QQ:38929568
QQ群:28048051JAVA游戏编程(满) 28047782(将满)

扫雷(3)是在扫雷(1.2)的基础上增加 完善了部分代码基本逻辑不变!
增加绘图,线程,时间等,使得游戏更好玩了,代码400行,比较适合初学者,可读性强,有详尽的代码注释。
数字键1标红旗,不确定,取消标记。方向键,数字键2468,控制光标上下左右移动!

工程文件已经打包上传到csdn,地址如下
download.csdn.net/user/kome2000/

程序运行如图

代码如下
Minesweeper.java这个类每有变化

 

 

 


//
//  cGame.java
//
//  Project: Minesweeper
//  Author(s): Gao Lei
//  Create: 2007-10-11


import  java.util.Random;                     // 得到 随机函数
import  javax.microedition.lcdui. * ;             // 写界面所需要的包


// 实现 Runnable 这个接口 必须创建Thread的对象,重写run()这个方法
class  cGame  extends  Canvas  implements  Runnable
{
    
//游戏状态
    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_SHOW        =  9;    //
    private static final int MINE_BOOM        = 10;    //踩到的雷
    private static final int MINE_GUESS_ERR    = 11;    //显示猜错了雷
    private static final int MINE_FLAG        = 12;    //设置红旗
    private static final int MINE_ASK        = 13;    //设置问号
    
//定义键值
    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         =  9;            //雷区的 行数        // 15
    private int     map_y         =  9;            //雷区的 列数        // 12
    private int     map_w         = 16;            //一个雷区的格子的宽度
    private int     map_h         = 16;            //一个雷区的格子的高度
    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     gameState     = STATEPLAY;    //游戏状态
    private int     s_width     = 0;            //屏幕尺寸 宽
    private int     s_height    = 0;            //屏幕尺寸 高
    private int     addMine        = 0;            //重新开始后雷数增加的个数
    private int     randPicNum    = 0;            //本关胜利的图片
    private int     palyTime    = 0;            //游戏时间
    private long    palyStartTime= 0;            //游戏开始时间
    private long    updates        = 0;            //更新次数
    private int[][] map;                        //雷区的地图数组 >=10 为雷, <10 为周围的雷数, 0位附近没有雷
    private int[][] map_show;                    //雷区的地图数组是否显示该位置的雷数//1显示//0不显示//9问号//10红旗
    private boolean isShowInfo    = false;        //是否显示游戏信息
    private String     strFlagNum     = "红旗数";
    
private String     strPalyTime = "时间";
    
private String[] gameInfo     = {"游戏中","失败 按0开始","胜利 按0开始"};
    
private Image[]    imgMine        = new Image[14];//格子周围的雷数0-8,9雷,10踩到的雷,11显示猜错了雷,12设置红旗,13设置问号
    private Image[] imgTime        = new Image[10];//数字图片
    private Image[] imgGameState= new Image[3];    //游戏状态图片//0游戏中//1失败//2胜利
    private Image[] imgGameWin    = new Image[7];    //游戏胜利图片
    private Image    imgBg;                        //背景图片
    
//定义一种大字体
    private Font     font         = Font.getFont( Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE );
    
private Thread    thread;                //创建线程对象

    cGame()
    
{
        setFullScreenMode(
true);        //设置游戏为全屏幕模式,该函数只能在支持midp2.0的手机上使用
        s_width = getWidth();            //得到屏幕尺寸     宽
        s_height= getHeight();            //得到屏幕尺寸     高
        try
        
{
            
//创建单张图片
            imgBg         = Image.createImage("/pics/bg.png");
            
//创建图片数组
            forint i=0; i<imgGameState.length; i++ )
            
{
                imgGameState[i]
=Image.createImage("/pics/game_over_"+i+".png");
            }

            
forint i=0; i<imgGameWin.length; i++ )
            
{
                imgGameWin[i]
=Image.createImage("/pics/game_win_"+i+".png");
            }

            
//另外一种创建图片数组的方法
            Image temp     = Image.createImage("/pics/time.png");    //将大图创建到临时Image对象中
            Graphics gn;                                        //创建临时绘图设备
            forint i=0; i<imgTime.length; i++ )                //大图将分割成多个小图
            {
                imgTime[i] 
= Image.createImage(1323);            //创建小图的大小
                gn = imgTime[i].getGraphics();                    //创建小图的大小的临时绘图设备
                
//在该设备上绘制大图temp,但 绘图设备比较小,只有小图那么大,大图多余部分不会被绘制出来
                gn.drawImage(temp, -i*130, gn.LEFT|gn.TOP);    //绘制大图时候的起点位置
            }

            temp     
= Image.createImage("/pics/tile.png");        //将大图创建到临时Image对象中
            forint i=0; i<imgMine.length; i++ )
            
{
                imgMine[i] 
= Image.createImage(1616);
                gn 
= imgMine[i].getGraphics();
                gn.drawImage(temp, 
-i*160, gn.LEFT|gn.TOP);
            }

            gn     
= null;
            temp 
= null;    
            System.gc();    
//通知垃圾回收机制,在需要时候会进行垃圾回收
        }
catch(Exception e){ e.printStackTrace(); }

        rePlay( 
0 );                                    //游戏初始化//重新游戏
        randPicNum    = rand.nextInt( imgGameWin.length );//本关胜利画面
        thread      = new Thread(this);                    //参数为 实现Runnable的类对象
        thread.start();                                    //启动线程
    }


    
public void run()
    
{
        
whiletrue )
        
{
            
try
            
{
                updates
++;                                //计算更新次数
                repaint();                                //刷屏
                thread.sleep(100);                        //线程休眠 100毫秒
            }
catch(Exception e)
            
{
                e.printStackTrace();
            }

        }

    }

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

    
public void paint(Graphics g)
    
{
        g.setClip(
00, s_width, s_height);        //设置参数描述的区域为操作区
        g.setColor(0x000000);                    //设置颜色为 黑色, 三个16进制数表示,RGB,如0x00ff00 为绿色
        g.fillRect(00, s_width, s_height);    //绘制一个实心矩形区域

        g.translate(
3-40);                    //设置屏幕偏移
        g.drawImage(imgBg, 00, g.LEFT|g.TOP);    //绘制图片到屏幕上,苗点 水平左对齐,垂直顶对齐
        g.drawImage(imgGameState[gameState], 74550);
        paintNum( g, 
2056, flagNum );            //显示剩余旗数
        if( gameState == STATEPLAY )
            palyTime 
= (int)((System.currentTimeMillis()-palyStartTime)/1000);
        paintNum( g,
11356, palyTime );        //显示游戏时间
        
        g.translate(
1595);                    //设置屏幕偏移
//        绘制雷区
        forint i=0; i<map_x; i++ )            
        
{
            
forint j=0; j<map_y; j++ )
            
{
                
if( map_show[i][j] == MINE_ON_SHOW )    //遍历地图数组 看该位置的雷数 是否应该显示
                {
                    
if(map[i][j]<9)        //显示周围的雷数
                    {
                        g.drawImage(imgMine[map[i][j]], j
*map_h, i*map_w, 0);
                    }

                    
else                //踩到雷了
                    {
                        g.drawImage(imgMine[MINE_BOOM], j
*map_h, i*map_w, 0);
                    }

                }

                
else if( map_show[i][j] == MINE_FLAG )        //显示红旗
                {
                    g.drawImage(imgMine[MINE_FLAG], j
*map_h, i*map_w, 0);
                }

                
else if( map_show[i][j] == MINE_ASK )        //显示问号
                {
                    g.drawImage(imgMine[MINE_ASK], j
*map_h, i*map_w, 0);
                }

                
else if( gameState != STATEPLAY )            //如果游戏结束    
                {
                    
if( map[i][j] >= MINE_SHOW )            //显示雷
                    {
                        
if( map_show[i][j] == MINE_ON_SHOW )//踩到雷了
                            g.drawImage(imgMine[MINE_BOOM], j*map_h, i*map_w, 0);
                        
else
                            g.drawImage(imgMine[MINE_SHOW], j
*map_h, i*map_w, 0);
                    }

                    
else if( map_show[i][j] == MINE_GUESS_ERR )//显示猜错了
                    {
                        g.drawImage(imgMine[MINE_GUESS_ERR], j
*map_h, i*map_w, 0);
                    }

                }

            }
    
        }

        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);    //绘制一个空心矩形框//为光标
        g.translate(-15-95);                        //设置屏幕偏移//恢复屏幕便宜
        g.translate(-340);                        //设置屏幕偏移//恢复屏幕便宜
        
        
if( isShowInfo || gameState == STATEWIN )    //如果游戏胜利
        {
            
if( updates %12 < 6 )                    //每6桢
                g.drawImage    ( imgGameWin[randPicNum],     045, g.LEFT|g.TOP );    //绘制游戏结束图片
            g.setFont    ( font );
            g.drawString( strFlagNum 
+":"+flagNum,    50100-20, g.LEFT|g.TOP );    //显示剩余旗数
            g.drawString( strPalyTime+":"+palyTime,    50100,     g.LEFT|g.TOP );    //显示游戏时间
            g.drawString( gameInfo[ gameState ],     50100+20, 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<0 )
                        key_y 
= map_x-1;
                }

            
break;

            
case KEY_NUM8:
            
case KEY_DOWN:
                
if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                    break;
                
else
                
{
                    key_y
++;
                    key_y 
%=map_x;
                }

            
break;

            
case KEY_NUM4:
            
case KEY_LEFT:
                
if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                    break;
                
else
                
{
                    key_x
--;
                    
if( key_x<0 )    
                        key_x 
= map_y-1;
                }

            
break;

            
case KEY_NUM6:
            
case KEY_RIGHT:
                
if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                    break;
                
else
                
{
                    key_x
++;
                    key_x 
%=map_y;
                }

            
break;

            
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] >=10 )//如果雷数>=10 该位置是雷,
                    {
                        isWinGame();
                        addMine        
= 0;
                        isShowInfo     
= true;
                        gameState 
= STATELOST;    //游戏失败
                    }

                }

            
break;

            
case KEY_NUM1:                        //设置红旗//问号//取消
                if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                    break;
                
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;
                            randPicNum    
= rand.nextInt( imgGameWin.length );//本关胜利画面
                        }

                        
else
                        
{
                            addMine        
= 0;
                            isShowInfo     
= true;
                            gameState      
= STATELOST;
                        }

                    }

                
break;
                
case MINE_FLAG:
                    flagNum
++;
                    map_show[key_y][key_x] 
= MINE_ASK;
                
break;
                
case MINE_ASK:
                    map_show[key_y][key_x] 
= MINE_OFF_SHOW;
                
break;
                }

            
break;
            
case KEY_NUM3:                        //是否显示游戏信息
                isShowInfo = !isShowInfo;
            
break;
            
case KEY_NUM0:                        //当按下 数字键 0
                rePlay( addMine );                    //重新开始游戏    
            break;
        }

        
        
/**
         * 重新执行 paint() 但该函数是立刻返回,也就是说他不会等待paint()执行完毕就返回了,
         * 如果 需要 paint()执行完毕才返回,可以使用serviceRepaints(),也可以两个都是用,但
         * repaint()应该在serviceRepaints()之前.
         
*/

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

    
    
boolean isWinGame()
    
{
        
boolean isWin = true;
        
forint i=0; i<map_x; i++ )            
        
{
            
forint j=0; j<map_y; j++ )
            
{
                
if( map_show[i][j] == MINE_FLAG )    //显示红旗
                {
                    
if( map[i][j] < 10 )            //地雷猜错了
                    {
                        map_show[i][j] 
= MINE_GUESS_ERR;
                        isWin 
= false;
                    }

                    
else
                    
{
                        rightNum 
++;
                    }

                }

            }

        }

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


    
//将三位数字以图片相识绘制到屏幕制定坐标
    void paintNum( Graphics g, int x, int y, int num )
    
{
        
if( num<=9 )    //个位数
        {
            g.drawImage(imgTime[
0]    , x      , y, 0);
            g.drawImage(imgTime[
0]    , x+13, y, 0);
            g.drawImage(imgTime[num], x
+26, y, 0);
        }

        
else if( num>=10 && num<=99 )    //两位数
        {
            g.drawImage(imgTime[
0]        , x      , y, 0);
            g.drawImage(imgTime[num
/10] , x+13, y, 0);
            g.drawImage(imgTime[num
%10] , x+26, y, 0);
        }

        
else                            //最多只保留三位数
        {
            num 
%= 1000;
            g.drawImage(imgTime[num
/100], x      , y, 0);
            num 
%= 100;
            g.drawImage(imgTime[num
/10] , x+13, y, 0);
            g.drawImage(imgTime[num
%10] , x+26, y, 0);
        }

    }

    
    
//该函数是一个递归函数,把当前位置设置成显示,并判断当前位置雷数是否为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 )
            
return;
        
else
            map_show[x][y] 
= MINE_ON_SHOW;

        
if( map[x][y] == 0 )
        
{
            
if( x-1 >= 0 )
            
{
                showMap( x
-1, y );
                
if( y-1 >= 0)        showMap( x-1, y-1 );
                
if( y+1 < map_y)    showMap( x-1, y+1 );
            }

            
if( y-1 >= 0)            showMap( x  , y-1 );
            
if( y+1 < map_y)        showMap( x  , y+1 );
            
if( x+1 < map_x )
            
{
                showMap( x
+1, y );
                
if( y-1 >= 0)        showMap( x+1, y-1 );
                
if( y+1 < map_y)    showMap( x+1, y+1 );
            }

        }

    }

    
    
//重新 开始 游戏
    public void rePlay( int add )
    
{
        key_x         
= map_x / 2;            //游戏初始时    光标所在雷区的格子位置
        key_y         = map_y / 2;            //游戏初始时    光标所在雷区的格子位置
        mine_num    += add;                    //雷区的雷数    不应该大于雷区的格子总数
        if(mine_num >= 64)
            mine_num 
= 64;                    //纠正雷数不能过多
        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;
        palyTime    
= 0;                    //游戏所用时间
        palyStartTime = System.currentTimeMillis();                //游戏开始时间
        
//布雷
        for(int i=0; i<mine_num; i++)        //随机mine_num个雷的位置
        {
            
int x = rand.nextInt( map_x );    //得到 随机数 雷格子的x方向位置
            int y = rand.nextInt( map_y );    //得到 随机数 雷格子的y方向位置
            if( map[x][y] >= 10)            //如果该位置已经是雷了,就要重新布雷
            {    
                i
--;
                
continue;
            }

            map[x][y] 
= 10;                    //否则 将该位置 设定为雷
            
//并在该雷的周围 的雷数都作+1操作
            
//以下判断为 边角判断,防止访问数组越界
            if( x-1 >= 0 )                    
            
{
                map[x
-1][y  ] += 1;
                
if( y-1 >= 0)        map[x-1][y-1+= 1;
                
if( y+1 < map_y)    map[x-1][y+1+= 1;
            }

            
if( y-1 >= 0)        map[x  ][y-1+= 1;
            
if( y+1 < map_y)    map[x  ][y+1+= 1;
            
if( x+1 < map_x )
            
{
                map[x
+1][y  ] += 1;
                
if( y-1 >= 0)        map[x+1][y-1+= 1;
                
if( y+1 < map_y)    map[x+1][y+1+= 1;
            }

        }

    }

}

 

屏幕大小 170*210 大于这个屏幕的手机都可以正常显示
如果机器建值不对,请修改后编译。

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值