一、 游戏实现的主要功能
1、用户可以选择级别并且重新开始游戏;
2、具有计时功能,即显示用户完成所有扫雷任务所使用的时间;
3、可显示剩余雷数,当用户插旗后雷数减1;
4、左键点击打开空格,右键点击插旗,左右键双击翻开周围未插旗的空格;
5、当找出全部的雷后,提示游戏结束。
二、游戏设计与实现
1 扫雷游戏界面设计
系统的整体布局为:空布局,采用了菜单、按钮、面板、标签等组件,菜单主要包括开始游戏、选择级别、退出等选项;按钮包括雷区按钮、笑脸按钮、数字按钮等,面板是标签的容器包括雷的数量、时间等信息。
package com.game.mine;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* 功能:游戏窗口
*/
public class GameFrame extends JFrame implements ActionListener
{
private GamePanel panel;
JMenuItem JEasy, JNormal, JHigh;
public GameFrame()
{
try
{
//窗口
this.setTitle("扫雷");
this.setLayout(null);
this.setBackground(Color.WHITE);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//菜单
JMenuBar JMine = new JMenuBar();
JMenu JGame = new JMenu("游戏");
JGame.setFont(new Font("宋体",Font.PLAIN,14));
JMenuItem JNew = JGame.add(" 开始");
JNew.setFont(new Font("宋体",Font.PLAIN,14));
JNew.addActionListener(this);
JNew.setActionCommand("new");
JGame.addSeparator();
this.JEasy = JGame.add("√ 初级");
this.JEasy.setFont(new Font("宋体",Font.PLAIN,14));
this.JEasy.addActionListener(this);
this.JEasy.setActionCommand("easy");
this.JNormal = JGame.add(" 中级");
this.JNormal.setFont(new Font("宋体",Font.PLAIN,14));
this.JNormal.addActionListener(this);
this.JNormal.setActionCommand("normal");
this.JHigh = JGame.add(" 高级");
this.JHigh.setFont(new Font("宋体",Font.PLAIN,14));
this.JHigh.addActionListener(this);
this.JHigh.setActionCommand("hard");
JGame.addSeparator();
JMenuItem JExit = JGame.add(" 退出");
JExit.setFont(new Font("宋体",Font.PLAIN,14));
JExit.addActionListener(this);
JExit.setActionCommand("exit");
JMine.add(JGame);
JMenu JHelp = new JMenu("帮助");
JHelp.setFont(new Font("宋体",Font.PLAIN,14));
JMenuItem JAbout = JHelp.add("关于");
JAbout.setFont(new Font("宋体",Font.PLAIN,14));
JAbout.addActionListener(this);
JAbout.setActionCommand("about");
JMine.add(JHelp);
this.setJMenuBar(JMine);
//面板
this.panel = new GamePanel();
this.add(this.panel);
//显示
this.panel.setLevel(this.panel.EASY);
this.setSize(this.panel.getWidth() + 15,this.panel.getHeight() + 60);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
catch(Exception e)
{
JOptionPane.showMessageDialog(this,"程序出现异常错误,即将退出!\r\n\r\n"+ e,
"提示",JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
}
@Override
public void actionPerformed(ActionEvent e)
{
String command = e.getActionCommand();
if("new".equals(command))
{
this.panel.newGame();
}
else if("easy".equals(command))
{
this.JEasy.setText("√ 初级");
this.JNormal.setText(" 中级");
this.JHigh.setText(" 高级");
this.panel.setLevel(this.panel.EASY);
this.setSize(this.panel.getWidth() + 15,this.panel.getHeight() + 60);
this.setLocationRelativeTo(null);
}
else if("normal".equals(command))
{
this.JEasy.setText(" 初级");
this.JNormal.setText("√ 中级");
this.JHigh.setText(" 高级");
this.panel.setLevel(this.panel.NORMAL);
this.setSize(this.panel.getWidth() + 15,this.panel.getHeight() + 60);
this.setLocationRelativeTo(null);
}
else if("hard".equals(command))
{
this.JEasy.setText(" 初级");
this.JNormal.setText(" 中级");
this.JHigh.setText("√ 高级");
this.panel.setLevel(this.panel.HARD);
this.setSize(this.panel.getWidth() + 15,this.panel.getHeight() + 60);
this.setLocationRelativeTo(null);
}
else if("exit".equals(command))
{
System.exit(0);
}
else if("about".equals(command))
{
JOptionPane.showMessageDialog(this,
"1、左键用于打开安全的格子,推进游戏进度;右键用于标记地雷,以辅助判断,或为接下来的双击做准备;\n" +
"双击在一个数字周围的地雷标记完时,相当于对数字周围未打开的方块均进行一次左键单击操作:\n" +
"2、左键单击:在判断出不是雷的方块上按下左键,可以打开该方块。如果方块上出现数字,则该数字表示\n" +
"其周围3×3区域中的地雷数(一般为8个格子,对于边块为5个格子,对于角块为3个格子。所以扫雷中最大\n" +
"的数字为8);如果方块上为空(相当于0),则可以递归地打开与空相邻的方块;如果踩雷,则游戏结束。\n" +
"3、右键单击:在判断为地雷的方块上按下右键,可以标记地雷(显示为小红旗)。重复一次或两次操作可\n" +
"标记?或取消标记。\n" +
"4、双击:同时按下左键和右键完成双击。当双击位置周围已标记雷数等于该位置数字时操作有效,相当于\n" +
"对该数字周围未打开的方块均进行一次左键单击操作。地雷未标记完全时使用双击无效。若数字周围有标错\n" +
"的地雷,则游戏结束,标错的地雷上会显示一个判断错误标志。","提示",JOptionPane.INFORMATION_MESSAGE);
}
}
}
2 雷区的设计
GamePanel类是JPanel容器的子类,实现了ActionListener和MouseListener接口所创建的对象:GamePanel()构造方法是GamePanel类中最重要的成员之一。其中GamePanel类的主要方法如下:
(1)initGame()方法可根据参数提供的数据设置雷区的宽度、高度、雷的数目以及计时器置零、初始化游戏界面。
(2)loadImage()方法加载数字图片、空格图片、雷图片、小红旗图片以及各种需要的图片。
(3)setLevel(int level)方法根据传入的参数根据不同的游戏难度重新设置主面板尺寸、重新设置组件位置、重新设置雷数、重新放置地雷位置以及时间清零等。
(4)newGame()方法调用initGame()方法初始化界面然后开始新游戏。
(5)actionPerformed(ActionEvent e)是GamePanel类实现的ActionListener接口中的方法。当用户单击雷区中的中的某个方块时,游戏开始定时器开始计时,单击笑脸按钮初始游戏界面开始新游戏。
(6)mousePressed(MouseEvent e)方法是GamePanel类实现的MouseListener接口中的方法,当用户按下鼠标时如果游戏结束退出游戏,如果游戏未开始,开始新游戏。
(7)mouseReleased(MouseEvent e)方法是GamePanel类实现的MouseListener
接口中的方法,分别定义了鼠标左键或右键后程序所执行的动作。
(8)GamePanel()构造方法对面板,雷区组件以及游戏难度等进行初始化。
package com.game.mine;
import java.awt.*;
import java.util.Map;
import java.util.HashMap;
import java.awt.event.*;
import javax.swing.*;
/**
* 功能:游戏面板<br>
*/
public class GamePanel extends JPanel implements MouseListener,ActionListener
{
private final GameLogic logic;
/** 初级难度(9×9,10个地雷) */
final int EASY = 1;
/** 中级难度(16×16,40个地雷) */
final int NORMAL = 2;
/** 高级难度(30×16,99个地雷) */
final int HARD = 3;
/** 上次游戏难度 */
private int oldLevel = -1;
/** 网格行数 */
int gridRows;
/** 网格列数 */
int gridColumns;
/** 网格尺寸 */
final int gridSize = 30;
/** 地雷数 */
int mineNum;
/** 提示区面板 */
private final JPanel panelTip = new JPanel();
/** 地雷区面板 */
private final JPanel panelMine = new JPanel();
/** 笑脸按钮 */
private final JButton buttonFace = new JButton();
/** 地雷数提示标签 */
JLabel[] labelMineTip = new JLabel[3];
/** 时间提示标签 */
JLabel[] labelTimeTip = new JLabel[3];
/** 地雷按钮数组 */
JButton[][] buttonMine = new JButton[16][30];
/**
* 地雷信息数组
* number->地雷数:-1-地雷,0到8-周围地雷数
* flag->地雷状态:0-未打开,1-已打开,2-插小旗,3-插问号
*/
@SuppressWarnings("unchecked") //数组不支持泛型
Map<String,Integer>[][] mapMine = new Map[16][30];
/** 笑脸图片 */
private final ImageIcon[] imageIconFace = new ImageIcon[2];
/** 时间、雷数提示图片 */
ImageIcon[] imageIconNumberTip = new ImageIcon[10];
/** 数字图片 */
ImageIcon[] imageIconNumber = new ImageIcon[9];
/** 空白图片 */
ImageIcon imageIconBlank = new ImageIcon("res/resource/mine/blank.gif");
/** 方格图片 */
ImageIcon imageIconCell = new ImageIcon("res/resource/mine/cell.gif");
/** 红旗图片 */
ImageIcon imageIconFlag = new ImageIcon("res/resource/mine/flag.gif");
/** 问号图片 */
ImageIcon imageIconQuestion = new ImageIcon("res/resource/mine/question.gif");
/** 地雷图片 */
ImageIcon imageIconMine = new ImageIcon("res/resource/mine/mine.gif");
/** 爆炸图片 */
ImageIcon imageIconBomb = new ImageIcon("res/resource/mine/bomb.gif");
/** 找雷错误图片 */
ImageIcon imageIconWrongMine = new ImageIcon("res/resource/mine/wrong mine.gif");
/** 时间提示数字 */
int timeTip = 0;
/** 扫雷提示数字 */
int mineTip = 0;
/** 计时器 */
Timer timer = new Timer(1000,this);
/** 游戏是否开始(true-开始,false-未开始) */
boolean isStart = false;
/** 游戏是否结束(true-结束,false-未结束) */
boolean isGameOver = true;
public GamePanel()
{
//主面板初始化
this.setBackground(Color.LIGHT_GRAY);
this.setBorder(BorderFactory.createRaisedBevelBorder()); //边框凸起
this.setLayout(null); //自由布局
//提示区面板初始化
this.panelTip.setBackground(Color.LIGHT_GRAY);
this.panelTip.setBorder(BorderFactory.createLoweredBevelBorder()); //边框下凹
this.panelTip.setLayout(null);
this.add(this.panelTip);
//地雷区面板初始化
this.panelMine.setBackground(Color.LIGHT_GRAY);
this.panelMine.setBorder(BorderFactory.createLoweredBevelBorder()); //边框下凹
this.add(this.panelMine);
//地雷数提示标签初始化
for(int i=0;i<this.labelMineTip.length;i++)
{
this.labelMineTip[i] = new JLabel();
}
//笑脸按钮初始化
this.buttonFace.addActionListener(this); //事件监听只能添加一次,若多次添加会造成监听事件重复执行。
//时间提示标签初始化
for(int i=0;i<this.labelTimeTip.length;i++)
{
this.labelTimeTip[i] = new JLabel();
}
//地雷按钮初始化
for(int row=0;row<this.buttonMine.length;row++)
{
for(int column=0;column<this.buttonMine[0].length;column++)
{
this.buttonMine[row][column] = new JButton();
this.buttonMine[row][column].addMouseListener(this);
this.buttonMine[row][column].setName(row+"_"+column); //用名字来区分行列坐标
}
}
//地雷信息初始化
for(int row=0;row<this.mapMine.length;row++)
{
for(int column=0;column<this.mapMine[0].length;column++)
{
this.mapMine[row][column] = new HashMap<>();
this.mapMine[row][column].put("number",0); //0个雷
this.mapMine[row][column].put("flag",0); //未打开
}
}
//加载图片
this.loadImage();
//游戏逻辑
this.logic = new GameLogic(this);
//设置游戏难度并调整各组件大小
this.setLevel(this.EASY);
}
/**
* 功能:加载图片
*/
private void loadImage()
{
try
{
//笑脸图片
this.imageIconFace[0] = new ImageIcon("res/resource/mine/smile.gif");
this.imageIconFace[1] = new ImageIcon("res/resource/mine/ooo.gif");
//时间、雷数提示图片
for(int i=0;i<this.imageIconNumberTip.length;i++)
{
this.imageIconNumberTip[i] = new ImageIcon("res/resource/mine/c" + i + ".gif");
}
//数字图片
for(int i=0;i<this.imageIconNumber.length;i++)
{
this.imageIconNumber[i] = new ImageIcon("res/resource/mine/" + i + ".gif");
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
* 功能:初始化游戏
*/
private void initGame()
{
//重新设置参数
this.mineTip = this.mineNum;
this.logic.setNumberTip(this.mineNum,0);
this.timeTip = 0;
this.logic.setNumberTip(0,1);
this.isGameOver = false;
this.isStart = false;
this.timer.stop();
//重置所有的信息
this.logic.resetAll();
//随机生雷并记录周围地雷数
this.logic.randomMine();
}
/**
* 功能:设置游戏难度级别
*/
public void setLevel(int level)
{
//判断难度是否有变化
if(this.oldLevel == level)
{
return;
}
//记录难度
this.oldLevel = level;
/* 本次游戏难度 */
//开始调试
if(level == this.EASY)
{
this.gridRows = 9;
this.gridColumns = 9;
this.mineNum = 10;
}
else if(level == this.NORMAL)
{
this.gridRows = 16;
this.gridColumns = 16;
this.mineNum = 40;
}
else if(level == this.HARD)
{
this.gridRows = 16;
this.gridColumns = 30;
this.mineNum = 99;
}
/* 网格宽度 */
int gridsWidth = this.gridSize * (this.gridColumns);
/* 网格高度 */
int gridsHeight = this.gridSize * (this.gridRows);
//重新设置主面板尺寸
this.setBounds(0,0, gridsWidth + 22, gridsHeight + 66);
//重新设置提示区面板尺寸并清除上面的组件
this.panelTip.setBounds(8,8,this.getWidth() - 17,36);
this.panelTip.removeAll();
//重新设置地雷区面板尺寸并清除上面的组件
this.panelMine.setBounds(8,50, gridsWidth + 5, gridsHeight + 5);
this.panelMine.removeAll();
//重新设置笑脸按钮
this.buttonFace.setBounds((this.panelTip.getX() + this.panelTip.getWidth() - 34)/2,5,26,27);
this.buttonFace.setIcon(this.imageIconFace[0]);
this.buttonFace.setPressedIcon(this.imageIconFace[1]);
this.buttonFace.setBorder(null); //边框太难看,不要
this.panelTip.add(this.buttonFace);
//重新设置地雷数提示标签并显示总地雷数
this.labelMineTip[0].setBounds(8,7,13,23);
this.labelMineTip[1].setBounds(21,7,13,23);
this.labelMineTip[2].setBounds(34,7,13,23);
this.logic.setNumberTip(this.mineNum,0);
this.panelTip.add(this.labelMineTip[0]);
this.panelTip.add(this.labelMineTip[1]);
this.panelTip.add(this.labelMineTip[2]);
//重新设置时间提示标签并显示时间数
this.labelTimeTip[0].setBounds(this.panelTip.getX() + this.panelTip.getWidth() - 54,7,13,23);
this.labelTimeTip[1].setBounds(this.panelTip.getX() + this.panelTip.getWidth() - 41,7,13,23);
this.labelTimeTip[2].setBounds(this.panelTip.getX() + this.panelTip.getWidth() - 28,7,13,23);
this.logic.setNumberTip(0,1);
this.panelTip.add(this.labelTimeTip[0]);
this.panelTip.add(this.labelTimeTip[1]);
this.panelTip.add(this.labelTimeTip[2]);
//重新设置地雷区按钮
this.panelMine.setLayout(null);
for(int row=0;row<this.gridRows;row++)
{
for(int column=0;column<this.gridColumns;column++)
{
this.buttonMine[row][column].setIcon(this.imageIconCell);
this.buttonMine[row][column].setPressedIcon(this.imageIconBlank);
this.buttonMine[row][column].setBorder(null); //先不要边框,极其难看。但后面开始挖雷时又必须设置边框,要不更极其难看。
this.buttonMine[row][column].setBounds(column * this.gridSize + 2,row * this.gridSize + 2,this.gridSize,this.gridSize);
this.panelMine.add(this.buttonMine[row][column]);
}
}
//初始化游戏
this.initGame();
}
/**
* 功能:开始新游戏
*/
public void newGame()
{
this.initGame();
}
@Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == this.timer) //定时器
{
this.timeTip++;
this.logic.setNumberTip(this.timeTip,1);
}
else if(e.getSource() == this.buttonFace)
{
this.newGame();
}
}
/**
* 功能:鼠标按下事件监听
*/
@Override
public void mousePressed(MouseEvent e)
{
//游戏结束,无回应
if(this.isGameOver)
{
return;
}
//游戏未开始,开始游戏
if(!this.isStart)
{
this.isStart = true;
this.timer.start();
}
//判断是否鼠标左右键同时点击。该判断仅在鼠标按下事件中判断
if(e.getModifiersEx() == MouseEvent.BUTTON1_DOWN_MASK + MouseEvent.BUTTON3_DOWN_MASK)
{
Object Obj = e.getSource();
if(Obj instanceof JButton)
{
JButton jbMine = (JButton)Obj;
String[] location = jbMine.getName().split("_");
int row = Integer.parseInt(location[0]);
int column = Integer.parseInt(location[1]);
this.logic.openCellAround(row, column); //鼠标左右键同时点击会打开周围的格子
}
}
}
/**
* 功能:鼠标释放事件监听
*/
@Override
public void mouseReleased(MouseEvent e)
{
//游戏结束,无回应
if(this.isGameOver) {
return;
}
//游戏未开始,开始游戏
if(!this.isStart)
{
this.isStart = true;
this.timer.start();
}
Object obj = e.getSource();
//鼠标左键点击
if(e.getButton() == MouseEvent.BUTTON1)
{
if(obj instanceof JButton)
{
JButton jbMine = (JButton)obj;
String[] location = jbMine.getName().split("_");
int row = Integer.parseInt(location[0]);
int column = Integer.parseInt(location[1]);
this.logic.openCell(row,column); //打开该格子
}
}
else if(e.getButton() == MouseEvent.BUTTON3) //鼠标右键点击
{
if(obj instanceof JButton)
{
JButton jbMine = (JButton)obj;
String[] location = jbMine.getName().split("_");
int row = Integer.parseInt(location[0]);
int column = Integer.parseInt(location[1]);
if(this.mapMine[row][column].get("flag") == 0) //处于未打开状态插红旗
{
this.mapMine[row][column].put("flag",2);
this.buttonMine[row][column].setIcon(this.imageIconFlag);
this.buttonMine[row][column].setPressedIcon(this.imageIconFlag);
//更改地雷提示数字
this.mineTip--;
this.logic.setNumberTip(this.mineTip,0);
}
else if(this.mapMine[row][column].get("flag") == 2) //处于插红旗状态变问号
{
this.mapMine[row][column].put("flag",3);
this.buttonMine[row][column].setIcon(this.imageIconQuestion);
this.buttonMine[row][column].setPressedIcon(this.imageIconQuestion);
//更改地雷提示数字
this.mineTip++;
this.logic.setNumberTip(this.mineTip,0);
}
else if(this.mapMine[row][column].get("flag") == 3) //处于问号状态变未打开
{
this.mapMine[row][column].put("flag",0);
this.buttonMine[row][column].setIcon(this.imageIconCell);
this.buttonMine[row][column].setPressedIcon(this.imageIconBlank);
}
}
}
}
@Override
public void mouseClicked(MouseEvent e){}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
}
3 游戏逻辑设计
GameLogic类的主要方法以及功能如下。
(1)setNumberTip(int number,int flag)根据传入的参数设置地雷数和时间提示标签栏。
(3)setMineAreadisable()方法,当调用此方法时,雷区所有按钮均处于不可用效果,及点击失效状态。
(2)resetAll()重置所有地雷信息。
(3)randomMine()随机生成并记录周围地雷数。
(4)countMineAround(int _row,int _column)计算传入坐标周围8个格子的雷数。
(5)showMine()显示所有的雷。
(6)openCell(int _row,int _column)打开传入坐标位置的单元格,并判断是否踩雷。如果踩雷就显示所有雷同时结束本局游戏,如果踩到空格采用递归法继续向周围格子判断地雷数,直到无空白格。
(7)openCellAround(int _row,int _column)当左右键同时点击单元格时,如果单元格周围的雷都已经正确标记时,打开周围非雷格子,如果标记错误游戏结束,显示标记错误的单元格并显示所有雷。
(8)GameOver()当未被打开的格子数等于雷数,游戏结束并弹出提示框“恭喜取得胜利”。
package com.game.mine;
import java.awt.*;
import javax.swing.JOptionPane;
import javax.swing.BorderFactory;
public class GameLogic
{
private final GamePanel panel;
public GameLogic(GamePanel gamePanel)
{
this.panel = gamePanel;
}
/**
* 功能:设置地雷数与时间提示
* 参数:number -> 要显示的数字
* 参数:flag -> 标识(0-地雷数,1-时间)
*/
public void setNumberTip(int number,int flag)
{
if(number <0){number = 0;}
//将数字转换为3位长字符串,前面不足补零
String tip = "000" + number;
tip = tip.substring(tip.length() - 3);
if(flag == 0) //显示剩余地雷数
{
if(number > this.panel.mineNum)
{
number = this.panel.mineNum;
}
for(int i=0;i<3;i++)
{
this.panel.labelMineTip[i].setIcon(this.panel.imageIconNumberTip[Integer.parseInt(tip.substring(i,i+1))]);
}
}
else if(flag == 1) //显示游戏时间
{
if(number > 999)
{
number = 999;
}
for(int i=0;i<3;i++)
{
this.panel.labelTimeTip[i].setIcon(this.panel.imageIconNumberTip[Integer.parseInt(tip.substring(i,i+1))]);
}
}
}
/**
* 功能:模拟雷区组件不可用效果
* number->地雷数:-1-地雷,0到8-周围地雷数
* flag->地雷状态:0-未打开,1-已打开,2-插小旗,3-插问号
*/
public void setMineAreaDisable()
{
for(int row = 0; row<this.panel.gridRows; row++)
{
for(int column = 0; column<this.panel.gridColumns; column++)
{
if(this.panel.mapMine[row][column].get("flag") == 0)
{
this.panel.buttonMine[row][column].setIcon(this.panel.imageIconCell);
this.panel.buttonMine[row][column].setPressedIcon(this.panel.imageIconCell);
}
}
}
}
/**
* 功能:重置所有的信息
*/
public void resetAll()
{
//重置地雷按钮
for(int row = 0; row<this.panel.gridRows; row++)
{
for(int column = 0; column<this.panel.gridColumns; column++)
{
this.panel.buttonMine[row][column].setIcon(this.panel.imageIconCell);
this.panel.buttonMine[row][column].setPressedIcon(this.panel.imageIconBlank);
this.panel.buttonMine[row][column].setBorder(null);
}
}
//重置地雷信息
for(int row = 0; row<this.panel.gridRows; row++)
{
for(int column = 0; column<this.panel.gridColumns; column++)
{
this.panel.mapMine[row][column].put("number",0); //0个雷
this.panel.mapMine[row][column].put("flag",0); //未打开
}
}
}
/**
* 功能:随机生雷并记录周围地雷数
*/
public void randomMine()
{
//随机生成地雷
for(int i=0;i<this.panel.mineNum;)
{
int row = (int)(Math.random() * this.panel.gridRows);
int column = (int)(Math.random() * this.panel.gridColumns);
//判断该位置是否已经有雷
if(this.panel.mapMine[row][column].get("number") != -1)
{
this.panel.mapMine[row][column].put("number",-1);
i++;
}
}
//记录周围的雷数
for(int row = 0; row<this.panel.gridRows; row++)
{
for(int column = 0; column<this.panel.gridColumns; column++)
{
if(this.panel.mapMine[row][column].get("number") != -1)
{
this.panel.mapMine[row][column].put("number",this.countMineAround(row,column));
}
}
}
}
/**
* 功能:计算周围的雷数
* 备注:九宫格(上、下、左、右、左上、左下、右上、右下)
*/
private int countMineAround(int _row,int _column)
{
int count = 0;
for(int row=_row-1;row<=_row+1;row++)
{
if(row < 0 || row >= this.panel.gridRows)
{
continue;
} //行出边界了
for(int column=_column-1;column<=_column+1;column++)
{
if(column < 0 || column >= this.panel.gridColumns)
{
continue;
} //列出边界了
if(row == _row && column == _column)
{
continue;
} //自身不计算在内
if(this.panel.mapMine[row][column].get("number") == -1)
{
count++;
}
}
}
return count;
}
/**
* 功能:显示所有的雷
*/
public void showMine()
{
for(int row = 0; row<this.panel.gridRows; row++)
{
for(int column = 0; column<this.panel.gridColumns; column++)
{
if(this.panel.mapMine[row][column].get("number") == -1)
{
this.panel.buttonMine[row][column].setIcon(this.panel.imageIconMine);
this.panel.buttonMine[row][column].setPressedIcon(this.panel.imageIconMine);
this.panel.buttonMine[row][column].setBorder(BorderFactory.createLineBorder(Color.GRAY,1));
this.panel.mapMine[row][column].put("flag",1);
}
}
}
}
/**
* 功能:打开单元格
* 备注:调用递归法
*/
public void openCell(int _row,int _column)
{
//如果状态是已经打开或标注小红旗了就不往下判断了
if(this.panel.mapMine[_row][_column].get("flag") == 1 || this.panel.mapMine[_row][_column].get("flag") == 2)
{
return;
}
//设置该格子的边框为线条形
this.panel.buttonMine[_row][_column].setBorder(BorderFactory.createLineBorder(Color.GRAY,1));
if(this.panel.mapMine[_row][_column].get("number") == -1) //踩到地雷了
{
this.panel.mapMine[_row][_column].put("flag",1);
this.showMine(); //显示所有雷
this.panel.buttonMine[_row][_column].setIcon(this.panel.imageIconBomb); //本格子的雷特殊标记
this.panel.buttonMine[_row][_column].setPressedIcon(this.panel.imageIconBomb);
this.setMineAreaDisable();
this.panel.isGameOver = true;
this.panel.isStart = false;
this.panel.timer.stop();
}
else if(this.panel.mapMine[_row][_column].get("number") == 0) //踩到空白处
{
this.panel.mapMine[_row][_column].put("flag",1);
this.panel.buttonMine[_row][_column].setIcon(this.panel.imageIconNumber[0]);
this.panel.buttonMine[_row][_column].setPressedIcon(this.panel.imageIconNumber[0]);
//开始判断周围四面八方的地雷数
for(int row=_row-1;row<=_row+1;row++)
{
if(row < 0 || row >= this.panel.gridRows)
{
continue;
}
for(int column=_column-1;column<=_column+1;column++)
{
if(column < 0 || column >= this.panel.gridColumns)
{
continue;
}
if(row == _row && column == _column)
{
continue;
}
if(this.panel.mapMine[row][column].get("number") != -1)
{
openCell(row,column);
} //如果是空白需采用递归法继续向该格子的四面八方判断地雷数,直到无空白格
}
}
}
else //踩到数字处
{
this.panel.mapMine[_row][_column].put("flag",1);
this.panel.buttonMine[_row][_column].setIcon(this.panel.imageIconNumber[this.panel.mapMine[_row][_column].get("number")]);
this.panel.buttonMine[_row][_column].setPressedIcon(this.panel.imageIconNumber[this.panel.mapMine[_row][_column].get("number")]);
}
//判断游戏是否结束
if(this.GameOver())
{
this.setMineAreaDisable();
this.panel.isGameOver = true;
this.panel.isStart = false;
this.panel.timer.stop();
}
}
/**
* 功能:打开周围的单元格
* 备注:用于鼠标左右键同时点击事件
*/
public void openCellAround(int _row,int _column)
{
//仅状态是已经打开且周围地雷数大于零时才生效
if(this.panel.mapMine[_row][_column].get("flag") == 1 || this.panel.mapMine[_row][_column].get("number") > 0)
{
//如果它周围的雷都已经标记出来(插红旗),且没错误,那么它周围未打开的非雷格子都默认打开
//先判断它周围有几个插了红旗的地雷,是否正确
int count = 0;
for(int row=_row-1;row<=_row+1;row++)
{
if(row < 0 || row >= this.panel.gridRows)
{
continue;
}
for(int column=_column-1;column<=_column+1;column++)
{
if(column < 0 || column >= this.panel.gridColumns)
{
continue;
}
if(row == _row && column == _column)
{
continue;
}
if(this.panel.mapMine[row][column].get("flag") == 2) //插上小红旗了
{
if(this.panel.mapMine[row][column].get("number") == -1)
{
count++;
}
else
{
//完蛋了,标记错误,结束游戏
this.panel.buttonMine[row][column].setIcon(this.panel.imageIconWrongMine);
this.panel.buttonMine[row][column].setPressedIcon(this.panel.imageIconWrongMine);
this.panel.mapMine[row][column].put("flag",1);
this.showMine();
this.setMineAreaDisable();
this.panel.isGameOver = true;
this.panel.isStart = false;
this.panel.timer.stop();
}
}
}
}
if(count == this.panel.mapMine[_row][_column].get("number")) //当前格子附近的雷都已经挖出来了,那么默认它周围非雷的格子都打开。
{
for(int row=_row-1;row<=_row+1;row++)
{
if(row < 0 || row >= this.panel.gridRows)
{
continue;
}
for(int column=_column-1;column<=_column+1;column++)
{
if(column < 0 || column >= this.panel.gridColumns)
{
continue;
}
if(row == _row && column == _column)
{
continue;
}
if(this.panel.mapMine[row][column].get("flag") == 0 || this.panel.mapMine[row][column].get("flag") == 3)
{
this.openCell(row,column);
}
}
}
}
}
}
/**
* 功能:判断游戏是否结束
*/
public boolean GameOver()
{
//判断未被打开的方格数与雷数是否相等
int count = 0;
for(int row = 0; row<this.panel.gridRows; row++)
{
for(int column = 0; column<this.panel.gridColumns; column++)
{
if(this.panel.mapMine[row][column].get("flag") != 1)
{
count++;
}
}
}
if(count == this.panel.mineNum)
{
this.panel.isGameOver = true;
JOptionPane.showMessageDialog(null,"恭喜您取得了胜利!");
return true;
}
return false;
}
}
GameLogic类
package com.game.mine;
public class Mine {
public static void main(String[] args)
{
new GameFrame();
}
}
4程序流程图
4.1总体流程图
4.2雷区设计流程图
4.3扫雷逻辑图
5程序测试
5.1 程序运行效果图
5.2 游戏菜单界面
5.3 游戏运行界面图
来源链接:https://www.cnblogs.com/ice-cui/p/18831617
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
暂无评论内容