信息发布→ 登录 注册 退出

Java实现扫雷游戏详细代码讲解

发布时间:2026-01-11

点击量:
目录
  • 效果展示
    • 难度选择展示
    • 游戏界面展示
  • 代码展示
    • 主类:GameWin类
    • 底层地图MapBottom类
    • 顶层地图MapTop类
    • 底层数字BottomNum类
    • 初始化地雷BottomRay类
    • 工具GameUtil类
    • 难度选择GameSelect类
  • 项目结构
    • 程序界面布局
      • 总结

        大家好!上一期我们使用GUI技术写了一个简单的扫雷小游戏,今天对这个Java应用程序更新迭代,增加了难度选择等功能,修复了已知的几个问题。成为初学者学习的好项目!Java实现扫雷小游戏【完整版】

        效果展示

        难度选择展示

        游戏界面展示

        代码展示

        主类:GameWin类

        //主类
        package com.sxt;
        import javax.swing.*;
        import java.awt.*;
        import java.awt.event.MouseAdapter;
        import java.awt.event.MouseEvent;
        public class GameWin extends JFrame {
            int width = 2 * GameUtil.OFFSET + GameUtil.MAP_W * GameUtil.SQUARE_LENGTH;
            int height = 4 * GameUtil.OFFSET + GameUtil.MAP_H * GameUtil.SQUARE_LENGTH;
            Image offScreenImage = null;
            MapBottom mapBottom = new MapBottom();
            MapTop mapTop = new MapTop();
            GameSelect gameSelect = new GameSelect();
            //是否开始,false未开始,true开始
            boolean begin=false;
            void launch(){
                GameUtil.START_TIME=System.currentTimeMillis();
                this.setVisible(true);
                if(GameUtil.state==3){
                    this.setSize(500,500);
                }else {
                    this.setSize(width,height);
                }
                this.setLocationRelativeTo(null);
                this.setTitle("Java扫雷小游戏");
                this.setDefaultCloseOperation(EXIT_ON_CLOSE);
                //鼠标事件
                this.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        super.mouseClicked(e);
                        switch (GameUtil.state){
                            case 0 :
                                if(e.getButton()==1){
                                    GameUtil.MOUSE_X = e.getX();
                                    GameUtil.MOUSE_Y = e.getY();
                                    GameUtil.LEFT = true;
                                }
                                if(e.getButton()==3) {
                                    GameUtil.MOUSE_X = e.getX();
                                    GameUtil.MOUSE_Y = e.getY();
                                    GameUtil.RIGHT = true;
                                }
                            case 1 :
                            case 2 :
                                if(e.getButton()==1){
                                    if(e.getX()>GameUtil.OFFSET + GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W/2)
                                            && e.getX()<GameUtil.OFFSET + GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W/2) + GameUtil.SQUARE_LENGTH
                                            && e.getY()>GameUtil.OFFSET
                                            && e.getY()<GameUtil.OFFSET+GameUtil.SQUARE_LENGTH){
                                        mapBottom.reGame();
                                        mapTop.reGame();
                                        GameUtil.FLAG_NUM=0;
                                        GameUtil.START_TIME=System.currentTimeMillis();
                                        GameUtil.state=0;
                                    }
                                }
                                if(e.getButton()==2){
                                    GameUtil.state=3;
                                    begin=true;
                                }
                                break;
                            case 3:
                                if(e.getButton()==1){
                                    GameUtil.MOUSE_X = e.getX();
                                    GameUtil.MOUSE_Y = e.getY();
                                    begin = gameSelect.hard();
                                }
                                break;
                            default:
                        }
                    }
                });
                while (true){
                    repaint();
                    begin();
                    try {
                        Thread.sleep(40);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            void begin(){
                if(begin){
                    begin=false;
                    gameSelect.hard(GameUtil.level);
                    dispose();
                    GameWin gameWin = new GameWin();
                    GameUtil.START_TIME = System.currentTimeMillis();
                    GameUtil.FLAG_NUM=0;
                    mapBottom.reGame();
                    mapTop.reGame();
                    gameWin.launch();
                }
            }
            @Override
            public void paint(Graphics g) {
                if(GameUtil.state==3){
                    g.setColor(Color.lightGray);
                    g.fillRect(0,0,500,500);
                    gameSelect.paintSelf(g);
                }else {
                    offScreenImage = this.createImage(width, height);
                    Graphics gImage = offScreenImage.getGraphics();
                    //设置背景颜色
                    gImage.setColor(Color.lightGray);
                    gImage.fillRect(0, 0, width, height);
                    mapBottom.paintSelf(gImage);
                    mapTop.paintSelf(gImage);
                    g.drawImage(offScreenImage, 0, 0, null);
                }
            }
            public static void main(String[] args) {
                GameWin gameWin = new GameWin();
                gameWin.launch();
            }
        }

        底层地图MapBottom类

        //底层地图类
        package com.sxt;
        import java.awt.*;
        public class MapBottom {
            BottomRay bottomRay = new BottomRay();
            BottomNum bottomNum = new BottomNum();
            {
                bottomRay.newRay();
                bottomNum.newNum();
            }
            //重置游戏
            void reGame(){
                for (int i = 1; i <=GameUtil.MAP_W ; i++) {
                    for (int j = 1; j <=GameUtil.MAP_H ; j++) {
                        GameUtil.DATA_BOTTOM[i][j]=0;
                    }
                }
                bottomRay.newRay();
                bottomNum.newNum();
            }
            //绘制方法
            void paintSelf(Graphics g){
                g.setColor(Color.red);
                //画竖线
                for (int i = 0; i <= GameUtil.MAP_W; i++) {
                    g.drawLine(GameUtil.OFFSET + i * GameUtil.SQUARE_LENGTH,
                            3*GameUtil.OFFSET,
                            GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH,
                            3*GameUtil.OFFSET+GameUtil.MAP_H*GameUtil.SQUARE_LENGTH);
                }
                //画横线
                for (int i = 0; i <=GameUtil.MAP_H; i++){
                    g.drawLine(GameUtil.OFFSET,
                            3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH,
                            GameUtil.OFFSET+GameUtil.MAP_W*GameUtil.SQUARE_LENGTH,
                            3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH);
                }
                for (int i = 1; i <= GameUtil.MAP_W ; i++) {
                    for (int j = 1; j <= GameUtil.MAP_H; j++) {
                        //雷
                        if (GameUtil.DATA_BOTTOM[i][j] == -1) {
                            g.drawImage(GameUtil.lei,
                                    GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1,
                                    GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1,
                                    GameUtil.SQUARE_LENGTH - 2,
                                    GameUtil.SQUARE_LENGTH - 2,
                                    null);
                        }
                        //数字
                        if (GameUtil.DATA_BOTTOM[i][j] >=0) {
                            g.drawImage(GameUtil.images[GameUtil.DATA_BOTTOM[i][j]],
                                    GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 15,
                                    GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 5,
                                    null);
                        }
                    }
                }
                //绘制数字 剩余雷数,倒计时
                GameUtil.drawWord(g,""+(GameUtil.RAY_MAX-GameUtil.FLAG_NUM),
                        GameUtil.OFFSET,
                        2*GameUtil.OFFSET,30,Color.red);
                GameUtil.drawWord(g,""+(GameUtil.END_TIME-GameUtil.START_TIME)/1000,
                        GameUtil.OFFSET + GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W-1),
                        2*GameUtil.OFFSET,30,Color.red);
                switch (GameUtil.state){
                    case 0:
                        GameUtil.END_TIME=System.currentTimeMillis();
                        g.drawImage(GameUtil.face,
                                GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
                                GameUtil.OFFSET,
                                null);
                        break;
                    case 1:
                        g.drawImage(GameUtil.win,
                                GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
                                GameUtil.OFFSET,
                                null);
                        break;
                    case 2:
                        g.drawImage(GameUtil.over,
                                GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
                                GameUtil.OFFSET,
                                null);
                        break;
                    default:
                }
            }
        }

        顶层地图MapTop类

        //顶层地图类
        package com.sxt;
        import java.awt.*;
        public class MapTop {
            //格子位置
            int temp_x;
            int temp_y;
            //重置游戏
            void reGame(){
                for (int i = 1; i <=GameUtil.MAP_W ; i++) {
                    for (int j = 1; j <=GameUtil.MAP_H ; j++) {
                        GameUtil.DATA_TOP[i][j]=0;
                    }
                }
            }
            //判断逻辑
            void logic(){
                temp_x=0;
                temp_y=0;
                if(GameUtil.MOUSE_X>GameUtil.OFFSET && GameUtil.MOUSE_Y>3*GameUtil.OFFSET){
                    temp_x = (GameUtil.MOUSE_X - GameUtil.OFFSET)/GameUtil.SQUARE_LENGTH+1;
                    temp_y = (GameUtil.MOUSE_Y - GameUtil.OFFSET * 3)/GameUtil.SQUARE_LENGTH+1;
                }
                if(temp_x>=1 && temp_x<=GameUtil.MAP_W
                        && temp_y>=1 && temp_y<=GameUtil.MAP_H){
                    if(GameUtil.LEFT){
                        //覆盖,则翻开
                        if(GameUtil.DATA_TOP[temp_x][temp_y]==0){
                            GameUtil.DATA_TOP[temp_x][temp_y]=-1;
                        }
                        spaceOpen(temp_x,temp_y);
                        GameUtil.LEFT=false;
                    }
                    if(GameUtil.RIGHT){
                        //覆盖则插旗
                        if(GameUtil.DATA_TOP[temp_x][temp_y]==0){
                            GameUtil.DATA_TOP[temp_x][temp_y]=1;
                            GameUtil.FLAG_NUM++;
                        }
                        //插旗则取消
                        else if(GameUtil.DATA_TOP[temp_x][temp_y]==1){
                            GameUtil.DATA_TOP[temp_x][temp_y]=0;
                            GameUtil.FLAG_NUM--;
                        }
                        else if(GameUtil.DATA_TOP[temp_x][temp_y]==-1){
                            numOpen(temp_x,temp_y);
                        }
                        GameUtil.RIGHT=false;
                    }
                }
                boom();
                victory();
            }
            //数字翻开
            void numOpen(int x,int y){
                //记录旗数
                int count=0;
                if(GameUtil.DATA_BOTTOM[x][y]>0){
                    for (int i = x-1; i <=x+1 ; i++) {
                        for (int j = y-1; j <=y+1 ; j++) {
                            if(GameUtil.DATA_TOP[i][j]==1){
                                count++;
                            }
                        }
                    }
                    if(count==GameUtil.DATA_BOTTOM[x][y]){
                        for (int i = x-1; i <=x+1 ; i++) {
                            for (int j = y-1; j <=y+1 ; j++) {
                                if(GameUtil.DATA_TOP[i][j]!=1){
                                    GameUtil.DATA_TOP[i][j]=-1;
                                }
                                //必须在雷区当中
                                if(i>=1&&j>=1&&i<=GameUtil.MAP_W&&j<=GameUtil.MAP_H){
                                    spaceOpen(i,j);
                                }
                            }
                        }
                    }
                }
            }
            //失败判定  t 表示失败 f 未失败
            boolean boom(){
                if(GameUtil.FLAG_NUM==GameUtil.RAY_MAX){
                    for (int i = 1; i <=GameUtil.MAP_W ; i++) {
                        for (int j = 1; j <=GameUtil.MAP_H ; j++) {
                            if(GameUtil.DATA_TOP[i][j]==0){
                                GameUtil.DATA_TOP[i][j]=-1;
                            }
                        }
                    }
                }
                for (int i = 1; i <=GameUtil.MAP_W ; i++) {
                    for (int j = 1; j <=GameUtil.MAP_H ; j++) {
                        if(GameUtil.DATA_BOTTOM[i][j]==-1&&GameUtil.DATA_TOP[i][j]==-1){
                            GameUtil.state = 2;
                            seeBoom();
                            return true;
                        }
                    }
                }
                return false;
            }
            //失败显示
            void seeBoom(){
                for (int i = 1; i <=GameUtil.MAP_W ; i++) {
                    for (int j = 1; j <=GameUtil.MAP_H ; j++) {
                        //底层是雷,顶层不是旗,显示
                        if(GameUtil.DATA_BOTTOM[i][j]==-1&&GameUtil.DATA_TOP[i][j]!=1){
                            GameUtil.DATA_TOP[i][j]=-1;
                        }
                        //底层不是雷,顶层是旗,显示差错旗
                        if(GameUtil.DATA_BOTTOM[i][j]!=-1&&GameUtil.DATA_TOP[i][j]==1){
                            GameUtil.DATA_TOP[i][j]=2;
                        }
                    }
                }
            }
            //胜利判断  t 表示胜利 f 未胜利
            boolean victory(){
                //统计未打开格子数
                int count=0;
                for (int i = 1; i <=GameUtil.MAP_W ; i++) {
                    for (int j = 1; j <=GameUtil.MAP_H ; j++) {
                        if(GameUtil.DATA_TOP[i][j]!=-1){
                            count++;
                        }
                    }
                }
                if(count==GameUtil.RAY_MAX){
                    GameUtil.state=1;
                    for (int i = 1; i <=GameUtil.MAP_W ; i++) {
                        for (int j = 1; j <=GameUtil.MAP_H ; j++) {
                            //未翻开,变成旗
                            if(GameUtil.DATA_TOP[i][j]==0){
                                GameUtil.DATA_TOP[i][j]=1;
                            }
                        }
                    }
                    return true;
                }
                return false;
            }
            //打开空格
            void spaceOpen(int x,int y){
                if(GameUtil.DATA_BOTTOM[x][y]==0){
                    for (int i = x-1; i <=x+1 ; i++) {
                        for (int j = y-1; j <=y+1 ; j++) {
                            //覆盖,才递归
                            if(GameUtil.DATA_TOP[i][j]!=-1){
                                if(GameUtil.DATA_TOP[i][j]==1){GameUtil.FLAG_NUM--;}
                                GameUtil.DATA_TOP[i][j]=-1;
                                //必须在雷区当中
                                if(i>=1&&j>=1&&i<=GameUtil.MAP_W&&j<=GameUtil.MAP_H){
                                    spaceOpen(i,j);
                                }
                            }
                        }
                    }
                }
            }
            //绘制方法
            void paintSelf(Graphics g){
                logic();
                for (int i = 1; i <= GameUtil.MAP_W ; i++) {
                    for (int j = 1; j <= GameUtil.MAP_H; j++) {
                        //覆盖
                        if (GameUtil.DATA_TOP[i][j] == 0) {
                            g.drawImage(GameUtil.top,
                                    GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1,
                                    GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1,
                                    GameUtil.SQUARE_LENGTH - 2,
                                    GameUtil.SQUARE_LENGTH - 2,
                                    null);
                        }
                        //插旗
                        if (GameUtil.DATA_TOP[i][j] == 1) {
                            g.drawImage(GameUtil.flag,
                                    GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1,
                                    GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1,
                                    GameUtil.SQUARE_LENGTH - 2,
                                    GameUtil.SQUARE_LENGTH - 2,
                                    null);
                        }
                        //差错旗
                        if (GameUtil.DATA_TOP[i][j] == 2) {
                            g.drawImage(GameUtil.noflag,
                                    GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1,
                                    GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1,
                                    GameUtil.SQUARE_LENGTH - 2,
                                    GameUtil.SQUARE_LENGTH - 2,
                                    null);
                        }
                    }
                }
            }
        }

        底层数字BottomNum类

        //底层数字类
        package com.sxt;
        public class BottomNum {
            void newNum() {
                for (int i = 1; i <=GameUtil.MAP_W ; i++) {
                    for (int j = 1; j <=GameUtil.MAP_H ; j++) {
                        if(GameUtil.DATA_BOTTOM[i][j]==-1){
                            for (int k = i-1; k <=i+1 ; k++) {
                                for (int l = j-1; l <=j+1 ; l++) {
                                    if(GameUtil.DATA_BOTTOM[k][l]>=0){
                                        GameUtil.DATA_BOTTOM[k][l]++;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        

        初始化地雷BottomRay类

        //初始化地雷类
        package com.sxt;
        public class BottomRay {
            //存放坐标
            static int[] rays = new int[GameUtil.RAY_MAX*2];
            //地雷坐标
            int x,y;
            //是否放置 T 表示可以放置 F 不可放置
            boolean isPlace = true;
            //生成雷
            void newRay() {
                for (int i = 0; i < GameUtil.RAY_MAX*2 ; i=i+2) {
                    x= (int) (Math.random()*GameUtil.MAP_W +1);//1-12
                    y= (int) (Math.random()*GameUtil.MAP_H +1);//1-12
                    //判断坐标是否存在
                    for (int j = 0; j < i ; j=j+2) {
                        if(x==rays[j] && y==rays[j+1]){
                            i=i-2;
                            isPlace = false;
                            break;
                        }
                    }
                    //将坐标放入数组
                    if(isPlace){
                        rays[i]=x;
                        rays[i+1]=y;
                    }
                    isPlace = true;
                }
                for (int i = 0; i < GameUtil.RAY_MAX*2; i=i+2) {
                    GameUtil.DATA_BOTTOM[rays[i]][rays[i+1]]=-1;
                }
            }
        }
        

        工具GameUtil类

        //工具类,存放静态参数,工具方法
        package com.sxt;
        import java.awt.*;
        public class GameUtil {
            //地雷个数
            static int RAY_MAX = 100;
            //地图的宽
            static int MAP_W = 36;
            //地图的高
            static int MAP_H = 17;
            //雷区偏移量
            static int OFFSET = 45;
            //格子边长
            static int SQUARE_LENGTH = 50;
            //插旗数量
            static int FLAG_NUM = 0;
            //鼠标相关
            //坐标
            static int MOUSE_X;
            static int MOUSE_Y;
            //状态
            static boolean LEFT = false;
            static boolean RIGHT = false;
            //游戏状态 0 表示游戏中 1 胜利 2 失败 3 难度选择
            static int state = 3;
            //游戏难度
            static int level;
            //倒计时
            static long START_TIME;
            static long END_TIME;
            //底层元素  -1 雷 0 空 1-8 表示对应数字
            static int[][] DATA_BOTTOM = new int[MAP_W+2][MAP_H+2];
            //顶层元素  -1 无覆盖 0 覆盖 1 插旗 2 差错旗
            static int[][] DATA_TOP = new int[MAP_W+2][MAP_H+2];
            //载入图片
            static Image lei = Toolkit.getDefaultToolkit().getImage("imgs/lei.png");
            static Image top = Toolkit.getDefaultToolkit().getImage("imgs/top.gif");
            static Image flag = Toolkit.getDefaultToolkit().getImage("imgs/flag.gif");
            static Image noflag = Toolkit.getDefaultToolkit().getImage("imgs/noflag.png");
            static Image face = Toolkit.getDefaultToolkit().getImage("imgs/face.png");
            static Image over = Toolkit.getDefaultToolkit().getImage("imgs/over.png");
            static Image win = Toolkit.getDefaultToolkit().getImage("imgs/win.png");
            static Image[] images = new Image[9];
            static {
                for (int i = 1; i <=8 ; i++) {
                    images[i] = Toolkit.getDefaultToolkit().getImage("imgs/num/"+i+".png");
                }
            }
            static void drawWord(Graphics g,String str,int x,int y,int size,Color color){
                g.setColor(color);
                g.setFont(new Font("仿宋",Font.BOLD,size));
                g.drawString(str,x,y);
            }
        }

        难度选择GameSelect类

        //难度选择类
        package com.sxt;
        import javax.swing.*;
        import java.awt.*;
        public class GameSelect  {
            //判断是否点击到难度
            boolean hard(){
                if(GameUtil.MOUSE_X>100&&GameUtil.MOUSE_X<400){
                    if(GameUtil.MOUSE_Y>50&&GameUtil.MOUSE_Y<150){
                        GameUtil.level=1;
                        GameUtil.state=0;
                        return true;
                    }
                    if(GameUtil.MOUSE_Y>200&&GameUtil.MOUSE_Y<300){
                        GameUtil.level=2;
                        GameUtil.state=0;
                        return true;
                    }
                    if(GameUtil.MOUSE_Y>350&&GameUtil.MOUSE_Y<450){
                        GameUtil.level=3;
                        GameUtil.state=0;
                        return true;
                    }
                }
                return false;
            }
            void paintSelf(Graphics g){
                g.setColor(Color.BLACK);
                g.drawRoundRect(100,50,300,100,40,40);
                g.setColor(Color.GRAY);
                g.fillRoundRect(100,50,300,100,40,40);
                GameUtil.drawWord(g,"简单模式",185,110,30,Color.black);
                g.drawRoundRect(100,200,300,100,40,40);
                g.setColor(Color.CYAN);
                g.fillRoundRect(100,200,300,100,40,40);
                GameUtil.drawWord(g,"中等模式",185,260,30,Color.black);
                g.drawRoundRect(100,350,300,100,40,40);
                g.setColor(Color.PINK);
                g.fillRoundRect(100,350,300,100,40,40);
                GameUtil.drawWord(g,"困难模式",185,410,30,Color.black);
            }
            void hard(int level){
                switch (level){
                    case 1:
                        GameUtil.RAY_MAX = 10;
                        GameUtil.MAP_W = 9;
                        GameUtil.MAP_H = 9;
                        break;
                    case 2:
                        GameUtil.RAY_MAX = 25;
                        GameUtil.MAP_W = 14;
                        GameUtil.MAP_H = 14;
                        break;
                    case 3:
                        GameUtil.RAY_MAX = 45;
                        GameUtil.MAP_W = 20;
                        GameUtil.MAP_H = 14;
                        break;
                    default:
                }
            }
        }

        项目结构

        本程序共封装了六个类,分别是主类GameWin类,绘制底层地图和绘制顶层地图的类MapBottom类和MapTop类,绘制底层数字的类BottomNum类,以及初始化地雷的BottomRay类和工具GameUtil类,用于存静态参数和方法,最后用于难度选择的方法封装在GameSelect类中。

        程序界面布局

        不同的难度雷区格子数不同!

        总结

        游戏的设计类似windows扫雷,用户在图形化用户界面内利用鼠标监听事件标记雷区,左上角表示剩余雷的数量,右上角动态显示使用的时间。用户可选择中间组件按钮重新游戏。

        为了解决程序窗口闪动的问题,本程序采用了双缓冲技术。 在使用Java编写扫雷小游戏时遇到了很多问题,在解决问题时,确实对java的面向对象编程有了更加深入的理解。虽然GUI现在并没有很大的市场,甚至好多初学者已经放弃了学习GUI,但是利用GUI编程的过程对于培养编程兴趣,深入理解Java编程有很大的作用。

        本程序是初学者练习的好项目,欢迎大家指正!

        在线客服
        服务热线

        服务热线

        4008888355

        微信咨询
        二维码
        返回顶部
        ×二维码

        截屏,微信识别二维码

        打开微信

        微信号已复制,请打开微信添加咨询详情!