接下来是完成菜单栏的功能游戏菜单添加菜单监听器（com.panel/BombJMenuBar.java）在init()方法中插入// 开局事件处理 menuItemStart.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { mainframe.reStartGame(); } }); // 添加事件监听器 MenuListener listener = new MenuListener(mainframe); menuItemStart.addActionListener(listener); menuItemLow.addActionListener(listener); menuItemMid.addActionListener(listener); menuItemHigh.addActionListener(listener); menuItemOrder.addActionListener(listener); menuHeroLow.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { new Hero(mainframe); }}); menuHeroMid.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { new Hero1(mainframe); }}); menuHeroHigh.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { new Hero2(mainframe); }}); menuItemAbout.addActionListener(listener); // 后门外挂方便测试 menuItemHole.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { } }); menuItemExit.addActionListener(new ActionListener() { //加一个系统退出的处理监听 public void actionPerformed(ActionEvent e) { System.exit(0); } });编写等级菜单监听类（com.listener/MenuListener）// 等级菜单监听器 public class MenuListener implements ActionListener { JMenuItem jMenuItem; JTextField jTextField=new JTextField(); MainFrame mainframe; public MenuListener(MainFrame mainframe){ this.mainframe = mainframe; } public void actionPerformed(ActionEvent e) { if(e.getActionCommand().equals("开局(N)")){ this.mainframe.reStartGame(); } if(e.getActionCommand().equals("初级(B)")){ Tools.rows = 9; Tools.cols = 9; Tools.allcount = 10; Tools.currentLevel = Tools.LOWER_LEVEL; this.mainframe.reStartGame(); } if(e.getActionCommand().equals("中级(I)")){ Tools.rows = 16; Tools.cols = 16; Tools.allcount = 40; Tools.currentLevel = Tools.MIDDLE_LEVEL; this.mainframe.reStartGame(); } if(e.getActionCommand().equals("高级(E)")){ Tools.rows = 16; Tools.cols = 30; Tools.allcount = 99; Tools.currentLevel = Tools.HEIGHT_LEVEL; this.mainframe.reStartGame(); } if(e.getActionCommand().equals("自定义(C)")){ new UserDefined(mainframe); } if(e.getActionCommand().equals("关于扫雷(A)")){ new About(mainframe); } } }注意：在雷区BombJPanel.java中之前是把行和列写成固定的，实现初级、中级、高级时要进行下述修改：// MineLabel[][] labels = new MineLabel[9][9]; MineLabel[][] labels = new MineLabel[Tools.rows][Tools.cols];运行效果：自定义菜单项（com.dialog/UserDefined.java）需求分析：出现弹窗界面，数据校验不能超过三位，只能为数字。通过继承JDialog实现用户自定义对话框public class UserDefined extends JDialog { // 自定义 private static final long serialVersionUID = 1L; private JLabel jLabelHigh = new JLabel("高度： "); private JLabel jLabelWide = new JLabel("宽度： "); private JLabel jLabelBomb = new JLabel("雷数： "); private JLabel jLabelMessage = new JLabel(" "); private JTextField jTextFieldHigh; private JTextField jTextFieldWide; private JTextField jTextFieldBomb; private JPanel panel; private JButton buttonSure; private JButton buttonCancer; MainFrame mainFrame; public UserDefined(final MainFrame mainFrame) { super(mainFrame); this.mainFrame = mainFrame; this.setIconImage(Tools.getImageIcon().getImage()); jLabelMessage.setFont(new Font("楷书", Font.PLAIN, 12)); jLabelMessage.setForeground(Color.red); this.setTitle("自定义雷区"); this.add(getPanel()); this.add(jLabelMessage, BorderLayout.NORTH); this.setSize(new Dimension(200, 150)); this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); this.setLocationRelativeTo(null); this.setResizable(false); this.addWindowListener(new WindowAdapter() { @Override public void windowDeactivated(WindowEvent e) { mainFrame.reStartGame(); } }); this.setModal(true); this.setVisible(true); } public JPanel getPanel() { JPanel jPanel = new JPanel(); Border border1 = BorderFactory.createEmptyBorder(5, 20, 5, 5); panel = new JPanel(); panel.setLayout(new GridLayout(1, 2)); Box boxHigh = Box.createHorizontalBox(); jTextFieldHigh = new JTextField(Tools.rows + ""); jTextFieldHigh.setPreferredSize(new Dimension(30, 20)); jTextFieldHigh.addKeyListener(new KeyListener() { @Override public void keyReleased(KeyEvent e) { String text = jTextFieldHigh.getText(); Pattern pattern = Pattern.compile("^[0-9]{1,3}$"); Matcher matcher = pattern.matcher(text); if (!matcher.matches()) { jLabelMessage.setText("请输入数字，不能超过三位"); if (text.length() > 3) { jTextFieldHigh.setText(text.substring(0, 3)); } } } @Override public void keyTyped(KeyEvent e) { char ch = e.getKeyChar(); if ((ch < '0') || (ch > '9')) { jLabelMessage.setText("请输入数字，不能超过三位"); e.setKeyChar((char) 8); } else { jLabelMessage.setText(" "); } } @Override public void keyPressed(KeyEvent e) { } }); boxHigh.add(jLabelHigh); boxHigh.add(jTextFieldHigh); Box boxWide = Box.createHorizontalBox(); jTextFieldWide = new JTextField(Tools.cols + ""); jTextFieldWide.setPreferredSize(new Dimension(30, 20)); jTextFieldWide.addKeyListener(new KeyListener() { @Override public void keyReleased(KeyEvent e) { String text = jTextFieldWide.getText(); Pattern pattern = Pattern.compile("^[0-9]{1,3}$"); Matcher matcher = pattern.matcher(text); if (!matcher.matches()) { jLabelMessage.setText("请输入数字，不能超过三位"); if (text.length() > 3) { jTextFieldWide.setText(text.substring(0, 3)); } } } @Override public void keyTyped(KeyEvent e) { char ch = e.getKeyChar(); if ((ch < '0') || (ch > '9')) { jLabelMessage.setText("请输入数字，不能超过三位"); e.setKeyChar((char) 8); } else { jLabelMessage.setText(" "); } } @Override public void keyPressed(KeyEvent e) { } }); boxWide.add(jLabelWide); boxWide.add(jTextFieldWide); Box boxBomb = Box.createHorizontalBox(); jTextFieldBomb = new JTextField(Tools.bombCount + ""); jTextFieldBomb.setPreferredSize(new Dimension(30, 20)); jTextFieldBomb.addKeyListener(new KeyListener() { @Override public void keyReleased(KeyEvent e) { String text = jTextFieldBomb.getText(); Pattern pattern = Pattern.compile("^[0-9]{1,3}$"); Matcher matcher = pattern.matcher(text); if (!matcher.matches()) { jLabelMessage.setText("请输入数字，不能超过三位"); if (text.length() > 3) { jTextFieldBomb.setText(text.substring(0, 3)); } } } @Override public void keyTyped(KeyEvent e) { char ch = e.getKeyChar(); if ((ch < '0') || (ch > '9')) { jLabelMessage.setText("请输入数字，不能超过三位"); e.setKeyChar((char) 8); } else { jLabelMessage.setText(" "); } } @Override public void keyPressed(KeyEvent e) { } }); boxBomb.add(jLabelBomb); boxBomb.add(jTextFieldBomb); Box boxS = new Box(BoxLayout.Y_AXIS); boxS.add(boxHigh); boxS.add(Box.createVerticalStrut(8)); boxS.add(boxWide); boxS.add(Box.createVerticalStrut(8)); boxS.add(boxBomb); boxS.add(Box.createVerticalStrut(8)); boxS.setBorder(border1); Box boxT = new Box(BoxLayout.Y_AXIS); buttonSure = new JButton("确定"); UserDefinedListener definedListener = new UserDefinedListener(this, mainFrame); buttonSure.setPreferredSize(new Dimension(70, 30)); buttonSure.setMargin(new Insets(0, 2, 0, 2)); buttonSure.addActionListener(definedListener); buttonCancer = new JButton("取消"); buttonCancer.setMargin(new Insets(0, 2, 0, 2)); buttonCancer.setSize(new Dimension(70, 30)); buttonCancer.addActionListener(definedListener); boxT.add(buttonSure); boxT.add(Box.createVerticalStrut(25)); boxT.add(buttonCancer); boxT.setBorder(border1); panel.add(boxS); panel.add(boxT); Border border = BorderFactory.createEmptyBorder(3, 15, 5, 15); jPanel.setBorder(border); jPanel.add(panel); return jPanel; } }自定义窗口监听器（com.listener/UserDefinedListener.java）public class UserDefinedListener implements ActionListener { UserDefined userDefined; MainFrame mainFrame; public UserDefinedListener(UserDefined userDefined, MainFrame mainFrame) { this.mainFrame = mainFrame; this.userDefined = userDefined; } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == userDefined.getButtonCancer()) { userDefined.dispose(); mainFrame.reStartGame(); } else if (e.getSource() == userDefined.getButtonSure()) { String highT = userDefined.getjTextFieldHigh().getText(); Pattern pattern = Pattern.compile("^[0-9]{1,3}$"); Matcher matcher = pattern.matcher(highT); int row = 0; if (!matcher.matches()) { userDefined.getjLabelMessage() .setText("输入的行数范围应在9-30之间"); return; } else { row = Integer.parseInt(highT); if (row < 9 || row > 30) { userDefined.getjLabelMessage().setText( "输入的行数范围应在9-30之间"); return; } } String colT = userDefined.getjTextFieldWide().getText(); int col = 0; try { col = Integer.parseInt(colT); if (col < 9 || col > 30) { userDefined.getjLabelMessage().setText( "输入的列数范围应在9-30之间"); return; } } catch (Exception e2) { userDefined.getjLabelMessage().setText( "列数应该为数字且范围应在9-30之间"); return; } String mineT = userDefined.getjTextFieldBomb().getText(); int mine = 0; try { mine = Integer.parseInt(mineT); if (mine < 10) { mine = 10; } else { mine = Math.min(mine, Tools.rows * Tools.cols * 4 / 5); } } catch (Exception e3) { userDefined.getjLabelMessage().setText("雷数应该为数字"); return; } userDefined.dispose(); Tools.rows = row; Tools.cols = col; Tools.allcount = mine; mainFrame.reStartGame(); } } }运行效果：英雄榜弹出胜利窗口（com.dialog/Win.java）public class Win extends JDialog { MainFrame mainframe; private JTextField text; TreeSet<Own> LOWER = new TreeSet<Own>(); TreeSet<Own> MIDDLE = new TreeSet<Own>(); TreeSet<Own> HEIGHT = new TreeSet<Own>(); public Win(MainFrame mainframe){ this.mainframe = mainframe; this.setIconImage(Tools.getImageIcon().getImage()); this.setTitle("扫雷成功"); this.setLocationRelativeTo(null); this.setSize(200, 150); this.init(); this.setVisible(true); } private void init() { // 存放记入 JPanel panel = new JPanel(); panel.setLayout(new GridLayout(4,1)); JLabel label = new JLabel("好厉害！请留下你的大名"); panel.add(label); text = new JTextField(); panel.add(text); // times = mainframe.getTimer().getTimes(); JLabel labTime = new JLabel("你所使用的时间："+Tools.timecount); panel.add(labTime); JButton butys = new JButton("保存"); butys.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(Tools.currentLevel.equals("初级")){ if(Tools.time1>=Tools.timecount && Tools.time2>=Tools.timecount && Tools.time3>=Tools.timecount){ Tools.time3 = Tools.time2; Tools.name3 = Tools.name2; Tools.time2 = Tools.time1; Tools.name2 = Tools.name1; Tools.time1 = Tools.timecount; Tools.name1=text.getText(); }else if(Tools.time2>=Tools.timecount && Tools.time3>=Tools.timecount && Tools.time1<=Tools.timecount){ Tools.time3 = Tools.time2; Tools.name3 = Tools.name2; Tools.time2 = Tools.timecount; Tools.name2=text.getText(); }else if(Tools.time3>=Tools.timecount && Tools.time1<=Tools.timecount && Tools.time2<=Tools.timecount){ Tools.time3 = Tools.timecount; Tools.name3=text.getText(); } } if(Tools.currentLevel.equals("中级")){ if(Tools.time01>=Tools.timecount && Tools.time02>=Tools.timecount && Tools.time03>=Tools.timecount){ Tools.time03 = Tools.time02; Tools.name03 = Tools.name02; Tools.time02 = Tools.time01; Tools.name02 = Tools.name01; Tools.time01 = Tools.timecount; Tools.name01=text.getText(); }else if(Tools.time02>=Tools.timecount && Tools.time03>=Tools.timecount && Tools.time01<=Tools.timecount){ Tools.time03 = Tools.time02; Tools.name03 = Tools.name02; Tools.time02 = Tools.timecount; Tools.name02=text.getText(); }else if(Tools.time03>=Tools.timecount && Tools.time01<=Tools.timecount && Tools.time02<=Tools.timecount){ Tools.time03 = Tools.timecount; Tools.name03=text.getText(); } } if(Tools.currentLevel.equals("高级")){ if(Tools.time001>=Tools.timecount && Tools.time002>=Tools.timecount && Tools.time003>=Tools.timecount){ Tools.time003 = Tools.time002; Tools.name003 = Tools.name002; Tools.time002 = Tools.time001; Tools.name002 = Tools.name001; Tools.time001 = Tools.timecount; Tools.name001=text.getText(); }else if(Tools.time002>=Tools.timecount && Tools.time003>=Tools.timecount && Tools.time001<=Tools.timecount){ Tools.time003 = Tools.time002; Tools.name003 = Tools.name002; Tools.time002 = Tools.timecount; Tools.name002=text.getText(); }else if(Tools.time003>=Tools.timecount && Tools.time001<=Tools.timecount && Tools.time002<=Tools.timecount){ Tools.time003 = Tools.timecount; Tools.name003=text.getText(); } } Win.this.dispose(); } }); panel.add(butys); this.add(panel); } public JTextField getText() { return text; }运行效果：英雄榜类（com.dialog/Hero.java）英雄榜分为初级、中级、高级英雄榜；每个英雄榜类逻辑是一样的，通过胜利窗口的事件监听器判断是哪个等级的英雄榜数据，然后写入临时变量中保存。以此类推编写中级、高级英雄榜Hero1,Hero2，当然也可以只写一个类，不过需要多加一些判断条件。public class Hero extends JDialog{ private JLabel jlabel1; private JLabel jlabel2; private JLabel jlabel3; private JLabel jlabel4; private JLabel jlabel5; private JLabel jlabel6; private JLabel time1; private JLabel time2; private JLabel time3; private JLabel name1; private JLabel name2; private JLabel name3; private JButton jbutton1; private JButton jbutton2; private MainFrame mainframe; private JPanel jpanel; public Hero(MainFrame mainframe) { this.mainframe=mainframe; this.setIconImage(Tools.getImageIcon().getImage()); this.setTitle("初级英雄榜"); this.setVisible(true); this.setSize(220,220); this.setResizable(false); this.setLocationRelativeTo(mainframe); this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); init(); } public JLabel getJLabel1() { return jlabel1; } public void setJLabel1(JLabel jlabel1) { this.jlabel1 = jlabel1; } public JLabel getJLabel2() { return jlabel2; } public void setJLabel2(JLabel jlabel2) { this.jlabel2 = jlabel2; } public JLabel getJLabel3() { return jlabel3; } public void setJLabel3(JLabel jlabel3) { this.jlabel3 = jlabel3; } public JLabel getJLabel4() { return jlabel4; } public void setJLabel4(JLabel jlabel4) { this.jlabel4 = jlabel4; } public JLabel getJLabel5() { return jlabel5; } public void setJLabel5(JLabel jlabel5) { this.jlabel5 = jlabel5; } public JLabel getJLabel6() { return jlabel6; } public void setJLabel6(JLabel jlabel6) { this.jlabel6 = jlabel6; } public JLabel getTime1() { return time1; } public void setTime1(JLabel time1) { this.time1 = time1; } public JLabel getTime2() { return time2; } public void setTime2(JLabel time2) { this.time2 = time2; } public JLabel getTime3() { return time3; } public void setTime3(JLabel time3) { this.time3 = time3; } public JLabel getName1() { return name1; } public void setName1(JLabel name1) { this.name1 = name1; } public JLabel getName2() { return name2; } public void setName2(JLabel name2) { this.name2 = name2; } public JLabel getName3() { return name3; } public void setName3(JLabel name3) { this.name3 = name3; } public void init(){ HeroListener heroListener = new HeroListener(); jlabel1 = new JLabel(" 名次"); jlabel2 = new JLabel(" 成绩"); jlabel3 = new JLabel(" 玩家"); jlabel4 = new JLabel(" 第一名："); jlabel5 = new JLabel(" 第二名："); jlabel6 = new JLabel(" 第三名："); time1 = new JLabel(""+Tools.time1); time2 = new JLabel(""+Tools.time2); time3 = new JLabel(""+Tools.time3); name1 = new JLabel(" "+Tools.name1); name2 = new JLabel(" "+Tools.name2); name3 = new JLabel(" "+Tools.name3); jbutton1=new JButton("确定"); jbutton1.addActionListener(heroListener); jbutton2=new JButton("重新设置"); jbutton2.addActionListener(heroListener); jpanel=new JPanel(); Box box1 = Box.createVerticalBox(); box1.add(jlabel1); box1.add(Box.createVerticalStrut(10)); box1.add(jlabel4); box1.add(Box.createVerticalStrut(10)); box1.add(jlabel5); box1.add(Box.createVerticalStrut(10)); box1.add(jlabel6); Box box2 = Box.createVerticalBox(); box2.add(jlabel2); box2.add(Box.createVerticalStrut(10)); box2.add(time1); box2.add(Box.createVerticalStrut(10)); box2.add(time2); box2.add(Box.createVerticalStrut(10)); box2.add(time3); Box box3 = Box.createVerticalBox(); box3.add(jlabel3); box3.add(Box.createVerticalStrut(10)); box3.add(name1); box3.add(Box.createVerticalStrut(10)); box3.add(name2); box3.add(Box.createVerticalStrut(10)); box3.add(name3); Box box4 = Box.createHorizontalBox(); box4.add(jbutton1); box4.add(Box.createHorizontalStrut(20)); box4.add(jbutton2); Box box5 = Box.createHorizontalBox(); box5.add(box1); box5.add(box2); box5.add(box3); Box box6 = Box.createVerticalBox(); box6.add(Box.createVerticalStrut(20)); box6.add(box5); box6.add(Box.createVerticalStrut(20)); box6.add(box4); jpanel.add(box6); this.add(jpanel); } class HeroListener implements ActionListener{ public void actionPerformed(ActionEvent e){ String command = e.getActionCommand(); if(command.equals("确定")){ dispose(); } else { Tools.time1 = 999; Tools.time2 = 999; Tools.time3 = 999; Tools.name1 = " 匿名"; Tools.name2 = " 匿名"; Tools.name3 = " 匿名"; time1.setText(""+Tools.time1); name1.setText(Tools.name1); time2.setText(""+Tools.time2); name2.setText(Tools.name2); time3.setText(""+Tools.time3); name3.setText(Tools.name3); //dispose(); } } } }运行效果：帮助菜单关于扫雷（com.dialog/About.java）可以根据自己的需求添加想要的窗口内容。public class About extends JDialog { MainFrame mainframe; public About(MainFrame mainframe){ this.setIconImage(Tools.getImageIcon().getImage()); this.mainframe = mainframe; this.setTitle("关于扫雷"); this.setLocationRelativeTo(null); this.setResizable(false); this.setSize(200, 130); this.init(); this.setVisible(true); } private void init() { Box box = Box.createVerticalBox(); JPanel jpanel = new JPanel(); JLabel jlabel = new JLabel("扫雷 ©2022"); JLabel jlabel1 = new JLabel("作者：XG.孤梦"); JLabel jlabel2 = new JLabel("我的博客：https://www.xggm.top"); box.add(jlabel); box.add(Box.createVerticalStrut(10)); box.add(jlabel1); box.add(Box.createVerticalStrut(10)); box.add(jlabel2); jpanel.add(box); this.add(jpanel); } }运行效果：外挂后门（com.listener/MenuListener.java）可以在布雷完成之后就编写这个方法，目的为了方便测试，节省时间，点击外挂，就会将是雷的小方格图片替换成其他图片，比如弄成中间有个小黑点的小方格// 后门外挂方便测试 menuItemHole.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (Tools.isStart) { Tools.isHole = true; // 判断每一个雷块是否是雷 for (MineLabel[] mineLabel : BombJMenuBar.this.mainframe .getBombJPanel().getLabels()) { for (MineLabel m : mineLabel) { if (m.isMineTag()) { m.setIcon(Tools.hole); } } } } } });运行效果：最后一个Java实现扫雷项目就这样完成了。
布雷上一篇已经完成了界面UI的实现，接下来开始功能的实现。定义布雷类（com.tools/LayMine.java）（1）布雷采用随机生成的布雷方式，玩家第一次点击小方格不应该是雷，故布雷功能设计在玩家第一次左键时开始布雷。参数row和col是第一次点击的鼠标坐标。public class LayMine { /** * labels：存储方格的二维数据 * row：当前鼠标点击的x值 * col：当前鼠标点击的y值 */ public static void lay(MineLabel[][] labels, int row, int col) { int count = 0; Random random = new Random(); // 随机 while (count<Tools.allcount) { int x = random.nextInt(Tools.rows); int y = random.nextInt(Tools.cols); if(!labels[x][y].isMineTag() && (x !=row && y!= col)) { // 布雷 labels[x][y].setMineTag(true); count++; } } countBomb(labels); }（2）计算小方格周围雷的数量分析当前方格（x，y）周围方格的坐标：设 x 和 y的最大值分别为 X 和 Y ：([0, x+1]，[0, y+1])([x-1, x+1]，[0, y+1])([x-1, X]，[0, y+1])([0, x+1]，[y-1, y+1])([x-1, x+1]，[y-1, y+1])([x-1, X]，[y-1, y+1])([0, x+1]，[y-1, Y])([x-1, x+1]，[y-1, Y])([x-1, X]，[y-1, Y])当前点（x,y）的周围方格x值最小值或者为0，或者为x-1，且不能小于0，x的最小值取Math.max(0, x- 1)，x的最大值x+1，或者为X，且x+1最大不能超过X，故x的范围：Math.max(0, x - 1) 至 Math.min(Tools.rows - 1, x + 1)同理y的范围：Math.max(0, y - 1) 至 Math.min(Tools.cols - 1, y + 1)实现方法一理解简单，通俗易懂，代码执行效率较低，不推荐使用public static void countBomb(MineLabel[][] labels) { int count = 0; if (!mineLabel[i][j].isMine()) { // 计算雷块周围八个方向雷数 /** * 上 */ if (i > 0) { if (labels[i - 1][j].isMine()) { count++; } } /** * 左上 */ if (i > 0 && j>0) { if (labels[i - 1][j-1].isMine()) { count++; } } /** * 右上 */ if (i > 0&&j+1< Tools.cols) { if (labels[i - 1][j+1].isMine()) { count++; } } /** * 左 */ if (j>0) { if (labels[i][j-1].isMine()) { count++; } } /** * 右 */ if (j+1< Tools.cols) { if (labels[i][j+1].isMine()) { count++; } } /** * 左下 */ if (i+1< Tools.rows && j>0) { if (labels[i + 1][j-1].isMine()) { count++; } } /** * 下 */ if (i+1< Tools.rows) { if (labels[i + 1][j].isMine()) { count++; } } /** * 右下 */ if (i+1< Tools.rows && j+1< Tools.cols) { if (labels[i + 1][j+1].isMine()) { count++; } } } }实现方法二 /** * |计算周围雷数 */ public static void countBomb(MineLabel[][] labels) { int count = 0; for (int i = 0; i < Tools.rows; i++) { for (int j = 0; j < Tools.cols; j++) { count = 0; // 当前方格不是雷才计算周围雷的数量 if (!labels[i][j].isMineTag()) { for (int x = Math.max(i - 1, 0); x <= Math.min(i + 1, Tools.rows - 1); x++) { for (int y = Math.max(j - 1, 0); y <= Math.min(j + 1, Tools.cols - 1); y++) { if (labels[x][y].isMineTag()) { count++; } } } labels[i][j].setCountAround(count); } } } }编写测试类（test/TestBomb.java）测试布雷和计数是否准确public class TestBomb { /** * 测试类 */ public static void main(String[] args) { MainFrame mainframe = new MainFrame(); MineLabel[][] labels = mainframe.getBombJPanel().getLabels(); LayMine.lay(labels, 3, 3); // 假设当前鼠标点击位置【3,3】 LayMine.countBomb(labels); for (int i = 0; i < Tools.rows; i++) { for (int j = 0; j < Tools.cols; j++) { if (labels[i][j].isMineTag()) { labels[i][j].setIcon(Tools.mine); }else { int count = labels[i][j].getCountAround(); labels[i][j].setIcon(Tools.mineCount[count]); } } } } }运行效果鼠标事件监听添加鼠标事件监听（com.listener/MouListener）public class MouListener implements MouseListener { MainFrame mainframe; MineLabel[][] labels; Boolean isDoublePress = false;; public MouListener(MineLabel[][] labels, MainFrame mainframe) { this.labels = labels; this.mainframe = mainframe; }在 BombJPanel.java 中为雷区小方块添加事件监听 public BombJPanel(MainFrame mainframe) { this.mainframe = mainframe; //定义布局方式，网格布局 this.setLayout(new GridLayout(Tools.rows, Tools.cols)); listener = new MouListener(labels,mainframe); init(); } // 初始化 private void init() { // 实例化小方格 for(int i = 0;i<labels.length;i++) { for(int j = 0; j<labels[i].length;j++) { labels[i][j] = new MineLabel(i, j); labels[i][j].setIcon(Tools.blank); this.add(labels[i][j]); labels[i][j].addMouseListener(listener); } } // 实现边框效果 Border lowerBorder = BorderFactory.createLoweredBevelBorder(); Border emptyBorder = BorderFactory.createEmptyBorder(5, 5, 5, 5); //边框大小 CompoundBorder compoundBorder = BorderFactory.createCompoundBorder(emptyBorder, lowerBorder) ; this.setBorder(compoundBorder); this.setBackground(Color.LIGHT_GRAY); } }需求分析：鼠标操作包含：左键、右键、左右键同时按下、右键一次、右键两次、右键三次。鼠标按下鼠标左键按下时效果：（1）设置笑脸为惊叹（2）左右键同时按下：设置鼠标所在位置周围小方格为背景效果左键按下时效果：（1）设置笑脸为惊叹（2）如果未被展开的，则显示鼠标所在位置的小方格的背景（3）已被展开则不做处理鼠标右键按下时效果：（1）第一次按下：标记插上红旗（2）第二次按下：标记显示问号（3）第三次按下：还原 // 鼠标按下时 @Override public void mousePressed(MouseEvent e) { MineLabel mineLabel = (MineLabel) e.getSource(); // 获取事件源 int row = mineLabel.getRowx(); int col = mineLabel.getColy(); // 判断是否是鼠标双击（左右键）操作 if (e.getModifiersEx() == InputEvent.BUTTON1_DOWN_MASK + InputEvent.BUTTON3_DOWN_MASK) { isDoublePress = true; doublePress(row, col); // 鼠标左键按下事件 } else if (e.getModifiers() == InputEvent.BUTTON1_MASK && !mineLabel.isFlagTag()) { // 对没有被展开或标记的方格 if (!mineLabel.isExpendTag()) { // 鼠标左键按下背景 mineLabel.setIcon(Tools.mineCount[0]); } // 表情变惊讶 mainframe.getFaceJPanel().getLabelFace().setIcon(Tools.face2); // 鼠标右键按下事件 } else if (e.getModifiers() == InputEvent.BUTTON3_MASK && !mineLabel.isExpendTag()) { // 右键点击数为 0 时 if (mineLabel.getRightClickCount() == 0) { mineLabel.setIcon(Tools.flag); // 设置旗子标记 mineLabel.setRightClickCount(1); mineLabel.setFlagTag(true); Tools.bombCount--; mainframe.getFaceJPanel().setNumber(Tools.bombCount); // 改变计数区雷数图片 // 右键点击数为 1 时 } else if (mineLabel.getRightClickCount() == 1) { mineLabel.setIcon(Tools.ask); // 设置问号标记 mineLabel.setRightClickCount(2); mineLabel.setFlagTag(false); Tools.bombCount++; mainframe.getFaceJPanel().setNumber(Tools.bombCount); // 改变计数区雷数图片 // 第3次点击还原回 未标记状态 } else { mineLabel.setIcon(Tools.blank); mineLabel.setRightClickCount(0); mineLabel.setFlagTag(false); } } }FaceJPanel.java类中添加：// 计数器 根据当前旗子数计算剩余雷数 public void setNumber(int count) { int b = 0; if (count < 0) { b = 10; } else { b = count / 100; } int g = Math.abs(count) % 10; int s = Math.abs(count) / 10 % 10; labelCountG.setIcon(Tools.timeCount[g]); labelCountS.setIcon(Tools.timeCount[s]); labelCountB.setIcon(Tools.timeCount[b]); }鼠标释放需求分析：包含左右键双击释放，左键释放，鼠标右键弹起没有任何动作。鼠标左右键双击释放（1）如果当前方格（被点击的方格）没有标记，且之前未被展开，则还原成点击前的状态（外观）；（2）否则（已标记，或者已被展开），就判断方格周围雷的数量与周围被标记旗子的方格数是否相等，如果相等就展开周围的方格A.相等的情况有标记存在两种情况：标记正确：如下图对圆圈所在方格双击释放时将打开其周围的方格标记错误：会有惩罚，相当于触雷，游戏结束。即当前方格周围的雷全部并且正确标记，则会迅速打开当前方格周围未打开的方格，如果标记有错误，则进行惩罚B.不相等：还原小方格状态鼠标左键释放（1）如果是第一次点击，则布雷，且点击的方格不布雷，确保第一次点击不触雷。（2）如果踩到雷，则引发触雷，游戏结束（3）否则展开方格// 鼠标左右键同时按下 private void doublePress(int row, int col) { for (int x = Math.max(0, row - 1); x <= Math.min(Tools.rows - 1, row + 1); x++) { for (int y = Math.max(0, col - 1); y <= Math.min(Tools.cols - 1, col + 1); y++) { if (!labels[x][y].isExpendTag() && !labels[x][y].isFlagTag()) { int rightClickCount = labels[x][y].getRightClickCount(); // 对标记旗子或者展开的不做处理 if (rightClickCount == 1) { labels[x][y].setIcon(Tools.flag); } else { labels[x][y].setIcon(Tools.mineCount[0]); } } } } } // 左键展开方格 private void expand(int x, int y) { int count = labels[x][y].getCountAround(); if (!labels[x][y].isExpendTag() && !labels[x][y].isFlagTag()) { // 周围雷为0，递归调用继续展开 if (count == 0) { labels[x][y].setIcon(Tools.mineCount[count]); labels[x][y].setExpendTag(true); for (int i = Math.max(0, x - 1); i <= Math.min(Tools.rows -1, x + 1); i++) { for (int j = Math.max(0, y - 1); j <= Math.min(Tools.cols -1, y + 1); j++) { expand(i, j); } } } else { // 显示周围雷数 labels[x][y].setIcon(Tools.mineCount[count]); labels[x][y].setExpendTag(true); } } } @Override public void mouseReleased(MouseEvent e) { MineLabel mineLabel= (MineLabel) e.getSource(); // 当前方格 int row = mineLabel.getRowx(); int col = mineLabel.getColy(); // 鼠标双击释放（右键不做处理） if(isDoublePress) { isDoublePress = false; if (!mineLabel.isExpendTag() && !mineLabel.isFlagTag()) { backIcon(row, col); } else { boolean isEquals = isEquals(row, col); if (isEquals) { doubleExpend(row, col); } else { backIcon(row, col); } } mainframe.getFaceJPanel().getLabelFace().setIcon(Tools.face0); // 左键释放 }else if (e.getModifiers() == InputEvent.BUTTON1_MASK && !mineLabel.isFlagTag()) { if(!Tools.isStart) { // 第一次鼠标左键点击，在弹起事件中进行布雷 LayMine.lay(labels, row, col); // 布雷 Tools.isStart = true; //设置isStart=true,表示不是第一次点击了 mainframe.getTimer().start(); // 开启计时器 } if (mineLabel.isMineTag()) {//判断是否踩到地雷 bombAction(row, col); //如果踩到地雷，游戏结束，显示全部的地雷 mineLabel.setIcon(Tools.blood); mainframe.getFaceJPanel().getLabelFace().setIcon(Tools.face3); } else { mainframe.getFaceJPanel().getLabelFace().setIcon(Tools.face0); expand(row, col); } } //判断雷是否已全被清除完 isWin(); } // 左右键双击展开 private void doubleExpend(int i, int j) { for (int x = Math.max(0, i - 1); x <= Math.min(Tools.rows - 1, i + 1); x++) { for (int y = Math.max(0, j - 1); y <= Math.min(Tools.cols - 1, j + 1); y++) { if (labels[x][y].isMineTag()) { // 如果是雷 if (!labels[x][y].isFlagTag()) { // 没有旗子标记 bombAction(x, y); } } else { // 不是雷 if (!labels[x][y].isFlagTag()) { // 没有旗子标记 expand(x, y); } } } } } // 触雷 private void bombAction(int row, int col) { for (int i = 0; i < labels.length; i++) { for (int j = 0; j < labels[i].length; j++) { if (labels[i][j].isMineTag()) { // 是雷 if (!labels[i][j].isFlagTag()) { // 没有标记 labels[i][j].setIcon(Tools.mine0); } else { labels[i][j].setIcon(Tools.mine1); } } } } // 修改踩雷状态 Tools.isBoom = true; // 停止计时器 mainframe.getTimer().stop(); // 取消鼠标监听器 for (int i = 0; i < labels.length; i++) { for (int j = 0; j < labels[i].length; j++) { labels[i][j].removeMouseListener(this); } } } //还原方格显示效果（因为鼠标按下时显示背景） private void backIcon(int i, int j) { for (int x = Math.max(0, i - 1); x <= Math.min(Tools.rows - 1, i + 1); x++) { for (int y = Math.max(0, j - 1); y <= Math.min(Tools.cols - 1, j + 1); y++) { if (!labels[x][y].isFlagTag() && !labels[x][y].isExpendTag()) { int rightClickCount = labels[x][y].getRightClickCount(); if (rightClickCount == 2) { labels[x][y].setIcon(Tools.ask); } else { if(!labels[x][y].isFlagTag()){ labels[x][y].setIcon(Tools.blank); } } } } } } 计数区面板点击事件（1）计时（com.timer/Timers）在主窗体中添加计时器timer控件，并编写事件监听器 // 计时器 public class Timers implements ActionListener { private int times; MainFrame mainfame; public Timers(MainFrame mainfame){ this.mainfame = mainfame; } @Override public void actionPerformed(ActionEvent arg0) { // TODO Auto-generated method stub Tools.timecount++; if(Tools.timecount>999){ Tools.timecount=999; }else{ mainfame.getFaceJPanel().setTime(Tools.timecount); } } }在FaceJPanel.java中添加：// 计时器 public void setTime(int count) { int b = 0; if (count < 0) { b = 10; } else { b = count / 100; } int g = Math.abs(count) % 10; int s = Math.abs(count) / 10 % 10; labelTimeG.setIcon(Tools.timeCount[g]); labelTimeS.setIcon(Tools.timeCount[s]); labelTimeB.setIcon(Tools.timeCount[b]); }（2）“笑脸”事件处理（com.panel/FaceJPanel.java）public class FacelabelListener extends MouseAdapter{ @Override public void mousePressed(MouseEvent e) { if (e.getModifiers() == InputEvent.BUTTON1_MASK) { mainframe.getTimer().stop(); labelFace.setIcon(Tools.face1); } } @Override public void mouseReleased(MouseEvent e) { if (e.getModifiers() == InputEvent.BUTTON1_MASK) { mainframe.getTimer().start(); mainframe.reStartGame(); labelFace.setIcon(Tools.face0); } }（3）重新开始方法（com.main/mainFrame.java） BombJPanel bombJPanel = new BombJPanel(this); FaceJPanel faceJPanel = new FaceJPanel(this); public void reStartGame() { // 游戏重新开始方法 this.remove(faceJPanel); this.remove(bombJPanel); Tools.bombCount = Tools.allcount; Tools.timecount = 0; Tools.isStart = false; Tools.isBoom = false; faceJPanel = new FaceJPanel(this); bombJPanel = new BombJPanel(this); this.add(faceJPanel, BorderLayout.NORTH); this.add(bombJPanel); this.pack(); this.validate(); getTimer().stop(); }在的init方法中添加 private void init() { // 菜单栏 this.setJMenuBar(menuBar); BorderLayout layout = new BorderLayout(); this.setLayout(layout); // 计数区 this.add(faceJPanel,layout.NORTH); // 雷区 this.add(bombJPanel,layout.CENTER); }扫雷成功需求分析：把不是雷的方格全部展开，如果不是雷的方格全部展开了，但雷没被标记也算扫雷成功,以下等式成立即可。被展开的方格数量 = 所有方格数量 - 雷的数量private void isWin() { int expendCount = 0; for (int i = 0; i < labels.length; i++) { for (int j = 0; j < labels[i].length; j++) { if (labels[i][j].isExpendTag()) { expendCount++; } } } if (Tools.rows * Tools.cols - expendCount == Tools.allcount) { for (int i = 0; i < Tools.rows; i++) for (int j = 0; j < Tools.cols; j++) { if (mainframe.getBombJPanel().getLabels()[i][j].isMineTag() && !mainframe.getBombJPanel().getLabels()[i][j].isFlagTag()) { mainframe.getBombJPanel().getLabels()[i][j].setIcon(Tools.flag); } // 移除监听 mainframe.getBombJPanel().getLabels()[i][j] .removeMouseListener(mainframe.getBombJPanel() .getListener()); } mainframe.getFaceJPanel().getLabelFace().setIcon(Tools.face4); mainframe.getFaceJPanel().setNumber(0); mainframe.getTimer().stop(); new Win(mainframe); //成功后弹出英雄记录版 Tools.isStart = false; } } //判断方格周围雷的数量与周围被标记的方格数是否相等 private boolean isEquals(int i, int j) { int count = labels[i][j].getCountAround(); int flagCount = 0; for (int x = Math.max(0, i - 1); x <= Math.min(Tools.rows - 1, i + 1); x++) { for (int y = Math.max(0, j - 1); y <= Math.min(Tools.cols - 1, j + 1); y++) { if (labels[x][y].isFlagTag()) { flagCount++; } } } if (count == flagCount) { return true; }else { return false; } }
编译软件：IntelliJ IDEA / Eclipse环境: JDK1.5以上介绍：重写了一遍扫雷，代码更加规范，修复了一些小bug，优化了部分代码的算法，将各等级英雄榜独立运行效果图github项目地址：https://github.com/XG2020/Mine-sweeping[card-default width="100%" label="下载地址"][btn href="https://www.lanzoum.com/iUNza0527yhg" type="primary"]点击下载[/btn] 复制密码: [copy]xggm[/copy] [/card-default]
实现项目结构分析主界面主窗体（com.main/MainFrame.java）包含菜单栏、计数区panel和雷区panel。（1）构造方法public MainFrame() { init(); this.setIconImage(Tools.getImageIcon().getImage()); // 设置图标 this.setTitle("扫雷"); // 设置标题 this.setSize(new Dimension(220,300)); // 窗口大小 this.setResizable(false); // 这样让窗口不可放大 this.setLocationRelativeTo(null); this.setLocation(new Point(800,300)); // 设置窗口位置 this.setVisible(true); // 设置窗口显示 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口 }（2）初始化方法 private void init() { // 菜单栏 this.setJMenuBar(menuBar); }菜单栏（com.panel/BombJMenuBar.java）（1）添加菜单、菜单项等public class BombJMenuBar extends JMenuBar { // 菜单项 JMenu menuGame = new JMenu("游戏(G)"); JMenuItem menuItemStart = new JMenuItem("开局(N)"); JMenuItem menuItemLow = new JMenuItem("初级(B)"); JMenuItem menuItemMid = new JMenuItem("中级(I)"); JMenuItem menuItemHigh = new JMenuItem("高级(E)"); JMenu menuHero = new JMenu("英雄榜(T)"); JMenuItem menuHeroLow = new JMenuItem("初级英雄榜"); JMenuItem menuHeroMid = new JMenuItem("中级英雄榜"); JMenuItem menuHeroHigh = new JMenuItem("高级英雄榜"); JMenuItem menuItemOrder = new JMenuItem("自定义(C)"); JMenuItem menuItemExit = new JMenuItem("退出(X)"); JMenu menuHelp = new JMenu("帮助(H)"); JMenuItem menuItemAbout = new JMenuItem("关于扫雷(A)"); JMenuItem menuItemHole = new JMenuItem("外挂(W)"); // 需要在类中声明主窗体变量（属性）：作为菜单栏与主窗体交互的媒介 // 声明主窗体对象 MainFrame mainframe; // 编写构造函数和初始化界面方面 public BombJMenuBar(MainFrame mainframe) { this.mainframe = mainframe; init(); }（2）初始化方法 public void init() { // 设置快捷键 menuGame.setMnemonic('G'); // Alt + G menuItemStart.setMnemonic('N'); menuItemLow.setMnemonic('B'); menuItemMid.setMnemonic('I'); menuItemHigh.setMnemonic('E'); menuHero.setMnemonic('T'); menuItemOrder.setMnemonic('C'); menuItemExit.setMnemonic('X'); menuHelp.setMnemonic('H'); menuItemAbout.setMnemonic('A'); menuItemHole.setMnemonic('W'); // 把菜单添加到菜单栏 // 游戏菜单 this.add(menuGame); menuGame.add(menuItemStart); menuGame.addSeparator(); // 添加分隔线 menuGame.add(menuItemLow); menuGame.add(menuItemMid); menuGame.add(menuItemHigh); menuGame.addSeparator(); menuGame.add(menuHero); menuHero.add(menuHeroLow); menuHero.add(menuHeroMid); menuHero.add(menuHeroHigh); menuGame.addSeparator(); menuGame.add(menuItemOrder); menuGame.addSeparator(); menuGame.add(menuItemExit); // 帮助菜单 this.add(menuHelp); menuHelp.add(menuItemAbout); menuHelp.addSeparator(); menuHelp.add(menuItemHole); }运行雷区（com.panel/BombJPanel）雷区是由小方格组成，在众多小方格中随机布雷。（先设计MineLable）（1）MineLablepublic class MineLabel extends JLabel { private boolean mineTag = false; // 判断是否是雷 private boolean expendTag = false; // 判断雷块是否展开 private boolean flagTag = false; // 判断雷块是否插了旗子 private int rowx; // 雷块所在的行 private int coly; // 雷块所在的列 private int countAround; // 计算雷块周围的雷数 private int rightClickCount; // 记录右键点击次数 }（2）编写构造方法public class BombJPanel extends JPanel { // 定义属性 MineLabel[][] labels = new MineLabel[9][9]; // 声明主窗体对象 MainFrame mainframe; MouListener listener; public BombJPanel(MainFrame mainframe) { this.mainframe = mainframe; //定义布局方式，网格布局 this.setLayout(new GridLayout(Tools.rows, Tools.cols)); listener = new MouListener(labels,mainframe); init(); }程序写到这里很多地方都需要要到图片以及雷区行列数等，故定义工具类定义好一些可能在程序中多次用到且后期会改变的资源或者变量。见工具类Tools（3）初始化private void init() { // 实例化小方格 for(int i = 0;i<labels.length;i++) { for(int j = 0; j<labels[i].length;j++) { labels[i][j] = new MineLabel(i, j); labels[i][j].setIcon(Tools.blank); this.add(labels[i][j]); labels[i][j].addMouseListener(listener); } }创建一个合成边框，指定了用于外部和内部边缘的 border 对象在init() 里插入 // 实现边框效果 Border lowerBorder = BorderFactory.createLoweredBevelBorder(); Border emptyBorder = BorderFactory.createEmptyBorder(5, 5, 5, 5); //边框大小 CompoundBorder compoundBorder = BorderFactory.createCompoundBorder(emptyBorder, lowerBorder); this.setBorder(compoundBorder); this.setBackground(Color.LIGHT_GRAY);createLoweredBevelBorder();创建一个具有凹入斜面边缘的边框，将组件当前背景色的较亮的色度用于高亮显示，较暗的色度用于阴影。（在凹入边框中，阴影位于顶部，高亮显示位于其下。）BorderFactory.createEmptyBorder(5, 5, 5, 5);创建占用空间但不绘制的空边框，指定顶部、左侧、底部和右侧的宽度。运行在MainFrame的构造函数中添加 this.pack(); public MainFrame() { init(); ...... this.pack(); // 使控件更紧凑，窗口自动适应大小 ...... }运行计数区（com.panel/FaceJPanel）（1）定义雷的数量与时间显示public class FaceJPanel extends JPanel { // 雷数 private JLabel labelCountG = new JLabel(); // 个位 private JLabel labelCountS = new JLabel(); // 十位 private JLabel labelCountB = new JLabel(); // 百位 // 笑脸 private JLabel labelFace = new JLabel(); // 时间 private JLabel labelTimeG = new JLabel(); // 个位 private JLabel labelTimeS = new JLabel(); // 十位 private JLabel labelTimeB = new JLabel(); // 百位 // // 声明主窗体对象 MainFrame mainframe; （2）编写构造方法 public FaceJPanel(MainFrame mainframe) { this.mainframe = mainframe; this.setLayout(new BorderLayout()); this.setVisible(true); init(); }（3）初始化private void init() { JPanel panel = new JPanel(); // 布局盒子 BoxLayout boxLayout = new BoxLayout(panel, BoxLayout.LINE_AXIS); panel.setLayout(boxLayout); labelFace.addMouseListener(new FacelabelListener()); // 添加表情按钮监听器 // 添加计数区监听器 panel.addMouseListener(new MouseAdapter(){ public void mousePressed(MouseEvent arg0) { if(!Tools.isBoom) { labelFace.setIcon(Tools.face2); } } public void mouseReleased(MouseEvent arg0) { if(!Tools.isBoom) { labelFace.setIcon(Tools.face0); } } }); Icon icon0 = new ImageIcon("./image/d0.gif"); // 计算雷的每位数图片 Icon icon3 = new ImageIcon("./image/d" + Tools.allcount / 100 + ".gif");// 百位 Icon icon2 = new ImageIcon("./image/d" + Tools.allcount /10%10 + ".gif"); // 十位 Icon icon1 = new ImageIcon("./image/d" + Tools.allcount % 10 + ".gif"); // 个位 Icon iconSmile = new ImageIcon("./image/face0.gif"); // 雷数显示图片 labelCountG.setIcon(icon1); labelCountS.setIcon(icon2); labelCountB.setIcon(icon3); // 时间显示图片 labelTimeG.setIcon(icon0); labelTimeS.setIcon(icon0); labelTimeB.setIcon(icon0); // 表情显示图片 labelFace.setIcon(iconSmile); // 添加控件到panel panel.add(Box.createHorizontalStrut(2)); // 最左侧 panel.add(labelCountB); panel.add(labelCountS); panel.add(labelCountG); panel.add(Box.createHorizontalGlue()); // 添加水平方向的构件，占位 panel.add(labelFace); panel.add(Box.createHorizontalGlue()); panel.add(labelTimeB); panel.add(labelTimeS); panel.add(labelTimeG); panel.add(Box.createHorizontalStrut(2)); // 最右侧 // 实现边框效果 Border borderLow = BorderFactory.createLoweredBevelBorder(); // 内边框 Border borderEmpty = BorderFactory.createEmptyBorder(2, 2, 2, 2); Border borderCom1 = BorderFactory.createCompoundBorder(borderLow, borderEmpty); panel.setBorder(borderCom1); panel.setBackground(Color.LIGHT_GRAY); // 外边框 Border borderEmpty1 = BorderFactory.createEmptyBorder(5, 5, 0, 5); this.setBorder(borderEmpty1); this.setBackground(Color.LIGHT_GRAY); this.add(panel); }（4）在主窗体MainFrame中添加FaceJPanelpublic class MainFrame extends JFrame{ ...... private BombJMenuBar menuBar = new BombJMenuBar(this); FaceJPanel faceJPanel = new FaceJPanel(this); BombJPanel bombJPanel = new BombJPanel(this); ...... }在init方法中插入private void init() { ...... this.add(faceJPanel,layout.NORTH); ...... }运行工具类（com.tools/Tools）public class Tools { // 窗口图标 public static ImageIcon imageIcon = new ImageIcon("./image/icon.gif"); public static ImageIcon getImageIcon() { return imageIcon; } public static int rows = 9; // 雷区行数 public static int cols = 9; // 雷区列数 public static int timecount = 0; // 计时 public static int allcount = 10; // 所有雷的数量 public static int bombCount = allcount; // 剩余为未被标记雷数 public static boolean isBoom = false; // 是否踩雷 public static boolean isStart = false; // 是否开始 public static boolean isHole = false; // 是否开启后门外挂 // 排行榜 public static int time = 0; // 初级 public static int time1= 999; public static int time2= 999; public static int time3= 999; public static String name1="匿名"; public static String name2="匿名"; public static String name3="匿名"; // 中级 public static int time01= 999; public static int time02= 999; public static int time03= 999; public static String name01="匿名"; public static String name02="匿名"; public static String name03="匿名"; // 高级 public static int time001= 999; public static int time002= 999; public static int time003= 999; public static String name001="匿名"; public static String name002="匿名"; public static String name003="匿名"; // 游戏等级 public static final String LOWER_LEVEL = "初级"; public static final String MIDDLE_LEVEL = "中级"; public static final String HEIGHT_LEVEL = "高级"; public static final String CUSTOM_LEVEL = "自定义"; // 游戏当前等级 public static String currentLevel = LOWER_LEVEL; // 用来存放0-8地雷数字标签图片 public static ImageIcon mineCount[]; // 用来存放时间数字标签图片 public static ImageIcon timeCount[]; // 静态块 static { mineCount = new ImageIcon[9]; for (int i = 0; i <= 8; i++) { mineCount[i] = new ImageIcon("./image/" + i + ".gif"); } timeCount = new ImageIcon[11]; for (int i = 0; i < 10; i++) { timeCount[i] = new ImageIcon("./image/d" + i + ".gif"); } timeCount[10] = new ImageIcon("./image/d10.gif"); } // 笑脸表情标签图片 public static ImageIcon face0 = new ImageIcon("./image/face0.gif"); public static ImageIcon face1 = new ImageIcon("./image/face1.gif"); public static ImageIcon face2 = new ImageIcon("./image/face2.gif"); public static ImageIcon face3 = new ImageIcon("./image/face3.gif"); public static ImageIcon face4 = new ImageIcon("./image/face4.gif"); public static ImageIcon face5 = new ImageIcon("./image/face5.gif"); public static ImageIcon face6 = new ImageIcon("./image/face6.gif"); public static ImageIcon face7 = new ImageIcon("./image/face7.gif"); public static ImageIcon face8 = new ImageIcon("./image/face8.gif"); // 分别对应 mine雷标签图片 public static ImageIcon mine = new ImageIcon("./image/mine.gif"); public static ImageIcon mine0 = new ImageIcon("./image/mine0.gif"); public static ImageIcon mine1 = new ImageIcon("./image/mine1.gif"); //分别对应 ask问号标签图片 public static ImageIcon ask = new ImageIcon("./image/ask.gif"); public static ImageIcon ask1 = new ImageIcon("./image/ask1.gif"); public static ImageIcon iconTemp = new ImageIcon("./image/icon.gif"); public static Image icon = iconTemp.getImage(); public static ImageIcon blank = new ImageIcon("./image/blank.gif"); public static ImageIcon blood = new ImageIcon("./image/blood.gif"); public static ImageIcon error = new ImageIcon("./image/error.gif"); // 旗子标签图片 public static ImageIcon flag = new ImageIcon("./image/flag.gif"); // hole外挂标签图片 public static ImageIcon hole = new ImageIcon("./image/hole.gif"); }
游戏介绍扫雷游戏是一款windows经典的电脑休闲小游戏，办公室打发无聊时间用的，非常受人喜爱，扫雷就是要把所有非地雷的格子揭开即胜利，踩到地雷格子就算失败。让玩家们找回童年玩此款游戏的乐趣，在游戏中只要掌握一些小技巧，闯关就会变得容易很多哦！初步分析通过扫雷界面图片可以分析出：界面是一个窗口，有菜单栏、标题栏和游戏区。菜单栏里有游戏和帮助两个菜单。游戏菜单里有：开局、初级、中级、高级、自定义、扫雷英雄榜、退出等7个子菜单；1、在游戏菜单里能够设置游戏的重新开局, 也可以通过点击游戏区里的笑脸来重新开局2、设置游戏的级别，分为初中高三个游戏级别：初级是由9*9的方格组成，雷数为10，非雷数71；中级由16*16的方格组成，排有40个雷，非雷数为216；高级是由16*30的方格组成，其中有99个雷，非雷数为381；3、自定义功能能够让玩家重新定义游戏的布局（行列范围9~30）与雷数（少于格子数且不能全是雷）；4、表情 游戏仍然在继续的时候显示微笑的表情::(呵呵)，当鼠标按住方格的时候显示惊讶的表情::(惊讶)，踩中地雷的时候显示悲哀的表情::(泪)，当你成功排除了所有雷，就会显示酷的表情::(酷)。5、在你翻开第一个方块起，计算器就开始计时，直到你成功排掉所有地雷或者踩中地雷的时候就结束计时。游戏规则与玩法游戏规则扫雷就是要把所有非地雷的格子揭开即胜利，踩到地雷格子就算失败；游戏主区域由很多个方格组成；使用鼠标左键随机点击一个方格，方格即被打开并显示出方格中的数字；方格中数字则表示其周围的8个方格隐藏了几颗雷；如果点开的格子为空白格，即其周围有0颗雷，则其周围格子自动打开，如果其周围还有空白格，则会引发连锁反应；在你认为有雷的格子上，点击右键即可标记雷，如果不能确定，可以双击右键即标记问号；如果一个已打开格子周围所有的雷已经正确标出，则可以在此格上同时点击鼠标左右键以打开其周围剩余的无雷格。玩法左键单击：在判断出不是雷的方块上按下左键，可以打开该方块。如果方块上出现数字，则该数字表示其周围3×3区域中的地雷数（一般为8个格子，对于边块为5个格子，对于角块为3个格子，所以扫雷中最大的数字为8）；如果方块上为空（相当于0），则可以递归地打开与空相邻的方块；如果不幸触雷，则游戏结束。右键单击：在判断为地雷的方块上按下右键，可以标记地雷（显示为小红旗）。标记地雷后重复一次右击则标记（？），需要一次或两次操作右击来取消标雷）。双击：同时按下左键和右键完成双击。当双击位置周围已标记雷数等于该位置数字时操作有效，相当于对该数字周围未打开的方块均进行一次左键单击操作。地雷未标记完全时使用双击无效。若数字周围有标错的地雷，则游戏结束。需求分析扫雷的左键逻辑。扫雷中鼠标左键被用来打开当前地图上的方块，但是如果你仔细研究，就会发现方块被打开发生在鼠标左键抬起之后，而不是鼠标左键按下的时候，这一点非常重要。如果在已经开启的方块上点击鼠标是没有任何作用的，并且如果方块上方被标记为旗帜，则该方块也无法被鼠标左键开启，这也是为了防止误操作导致游戏意外结束。如果游戏在初始状态，鼠标左键的抬起事件会触发了雷区（或者称之为地图）的初始化以及方块打开操作，并开始计时。如果游戏处于运行状态，则要判断点击的位置是否为地雷，如果是地雷直接结束，否则执行默认的方块打开操作。鼠标右键主要是用来标记当前方块的属性，是地雷（旗帜）还是不确定（问号），这里需要注意的是标记的过程中，是鼠标点击的时候就进行了，而不是按键抬起之后。随着方块标记的转变，地雷的显示数量也随之改变。这里还有一个小细节，就是鼠标的右键操作并不会导致游戏开始计时，换句话说右键操作并不会让游戏进入运行状态。自动打开操作是扫雷游戏的基本规则。如果在双击（左右键）的位置存在一个数字，且周围 8 个方块上方已经被标记上了和数字相同的旗帜，则同时点击鼠标左右键会自动打开周围未标记的方块。方块被打开是在鼠标弹起操作后，双击按下只是显示相关方块的背景，给人的效果是方块被按下去。统计点击位置周围的标记数量，标记数量和显示数字一致的话，打开剩余方块。这里有个小细节，就是打开的时候并不是仅仅打开周围的 8 个方块，如果这 8 个方块中存在空白的情况，会触发成片开启的情况。软件功能框架图