Java实现扫雷小游戏【优化版】

🌕文章目录

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


🍊效果展示

🍁难度选择展示

在这里插入图片描述

🍁游戏界面展示

在这里插入图片描述


🍊代码展示

🍁主类: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编程有很大的作用。


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

 
友情链接
鄂ICP备19019357号-22