基于Java语言的扫雷游戏设计开发

一、 游戏实现的主要功能

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

© 版权声明
THE END
支持一下吧
点赞12 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容