首页 > 编程学习 > 基于Java的Windows扫雷游戏的设计与实现

基于Java的Windows扫雷游戏的设计与实现

发布时间:2022/10/1 9:13:28

目 录
摘 要 i
Abstract ii
1 绪论 1
1.1 课题背景及意义 1
1.2 开发工具的选用及介绍 1
1.3 选题目的和意义 2
1.4 本文主要研究的内容 2
2 需求分析 3
2.1 可行性分析 3
2.2 扫雷游戏功能描述 3
2.3 扫雷游戏用例图 4
2.4 扫雷游戏功能需求 4
2.5 扫雷游戏界面需求 5
2.6 扫雷游戏功能模块 6
3 游戏的概要分析与设计 7
3.1 设计构想 7
3.2 流程规划 8
3.3 界面规划 9
3.4 算法思想 9
4 游戏的详细设计 11
4.1 游戏初始化 11
4.2 雷区的布置 12
4.3 游戏中主要模块的介绍与使用 13
4.3.1 鼠标事件 13
4.3.2 地雷及雷区表面探测情况 14
4.3.3 清除未靠近地雷的格子 15
4.3.4 游戏难度的选择 16
4.3.5 菜单栏的功能 16
4.4 游戏的判断 16
4.4.1 游戏成功完成 16
4.4.2 游戏失败 16
4.5 类设计 17
4.5.1 MineGame类 17
4.5.2 Block类 18
4.5.3 BlockView类 19
4.5.4 Record类 20
4.5.5 ShowRecord类 21
4.5.6 MineArea类 22
4.5.7 LayMines类 23
5 游戏实现 25
5.1 游戏难度自定义 25
5.2 扫雷 28
5.2.1 玩家通过右键进行扫雷,并显示小红旗 28
5.2.2 玩家因触碰到雷而导致游戏结束 29
5.2.3 玩家扫雷成功 29
5.2.4 玩家游戏数据显示 30
5.3 程序打包发布过程 32
6 游戏测试结果 33
6.1 游戏难度自定义测试 33
6.2 扫雷测试 35
6.3 玩家游戏数据显示测试 37
6.4 游戏数据显示区测试 39
6.5 递归算法测试 43
结 论 44
参考文献 45
致 谢 47
外文原文 48
中文翻译 55
1.4本文主要研究的内容
扫雷游戏风靡全球,受到很多人的喜爱。我研究了这个扫雷游戏之后,发现有两个关键点:
第一点是鼠标事件的处理,包括鼠标点击菜单项中按钮的事件,点击雷区中任意一格的事件,点击界面上按钮的事件等。
第二点是初始化雷区,包括随机生成雷的位置,界面的初始化,计算周围雷数等。
具体要求如下:
(1)扫雷游戏分为三种难度,分别是初、中、高级,点击游戏左上角的“游戏”菜单后会出现“初级”、“中级”、“高级”三种选项。除此之外玩家还可以自定义难度。
(2)在玩家选择难度之后游戏将会改变界面大小,并改变雷的总数。当玩家点开第一个格子时雷区开始布雷同时计时开始。
(3)若玩家想放弃本局游戏并重新开始的话,可以通过点击扫雷信息统计区中的按钮,来结束当前的游戏并开始新的游戏。
(4)当玩家认为某个格没有雷,可以直接用鼠标单击这个格或者把鼠标放在该格子上按下空格键。如果玩家的判断是正确的,则会显示周围8个格子中地雷的总数,如果玩家判断错误,该格子下藏着雷的话,游戏就好结束,系统自动弹框提示玩家失败。
(5)若玩家认为某个格下有雷,在该格子上鼠标右键就可以标记一个玩家认为该格是雷的图标,也就是在格子上标记一个小红旗的图案。玩家每标记一个红旗(不管玩家的判断正确与否),界面上的计数区都会把雷数减一。
(6)当所有的雷都标记正确后,游戏会自动弹框显示“您真厉害,请输入您的名字,记录上榜!”。
(7)若玩家在游戏过程中单击了有雷的格子,游戏失败并且记时停止,游戏会将剩余的雷全部显示出来,自动弹框显示“你输了,请继续努力!”,之后玩家可以开始一局新的游戏。
(8)如果玩家不清楚游戏玩法,可以点击菜单栏上的帮助来获取游戏玩法及游戏窍门。
(9)对于自定义难度,系统会有一个阈值,行数在9到16之间,列数在9到30之间,地雷数在10到99之间。如果玩家设置的值超过该阈值,系统会自动将超过的数值变成阈值。
2.4扫雷游戏功能需求
本次扫雷设计需要实现的主要功能有:

(1)玩家可以选择合适的游戏难度

点击游戏界面左上角菜单栏中的“游戏”菜单,会出现出现“初级”、“中级”、“高级”、“自定义”四种选项。在玩家选择难度之后游戏将会改变界面尺寸,并改变雷的总数。前三种雷数和尺寸都是固定的,无需玩家设置,而自定义则是玩家自己定义的。

(2)进行扫雷游戏

游戏界面包括菜单栏,游戏数据显示区域以及雷区,当玩家点击雷区中的任意一格的时候游戏开始,雷区开始随机布雷,玩家需要在最短的时间内找出雷区中所有的地雷,并加以标识。

扫雷的基本操作包括鼠标左键单击和右键单击两种。其中左键用来点开玩家认为不是雷的格子,右键标记玩家认为是雷的格子。

左键单击:玩家在判断出该格子下没有雷时单击该格子,可以将该格子点开。如果该格子周围有雷就会显示周围雷的数目(由于周围最多只有8个格子,所以最多只能显示8);如果格子上什么也不显示(也就是为空),系统就会自动调用递归的方法打开附近的格子。如果点到了有地雷的格子,那么游戏结束,系统显示所有雷的位置。

右键单击:玩家可以通过使用鼠标右键单击来标记自己认为是雷的格子,通过标记可以有效的提高扫雷游戏的效率。本文转载自http://www.biyezuopin.vip/onews.asp?id=11244再次右击该格子可以取消标记,且格子被标记的时候鼠标单击无效(防止玩家误操作导致游戏失败)。

(3)游戏计时

当点击雷区任意一个格子的时候雷区开始布雷同时计时开始,计时标准是一秒增加1。如果游戏失败时,则计时停止。如果玩家开始了新游戏,计时也会重新开始。

(4)标记地雷

当玩家认为格子下有雷时可以右击格子来标记该格子,被标记的格子显示小红旗。再次右击可以取消标记,且格子被标记的时候鼠标单击无效(防止玩家误操作导致游戏失败)。如果玩家标识的地雷的数量超过了该难度下规定的雷数,计数区会以负数来显示超过的雷数并且游戏不会结束。

(5)统计功能

当玩家游戏胜利后会弹框提示并可以输入玩家的名字,确认后本次游戏的记录会被保存。玩家可以点击菜单栏上的“游戏”菜单,再点击其中的“扫雷榜”即可查看游戏的最佳记录。

(6)退出

点击关闭按钮可以结束游戏。

package cn.itbaizhan.mine;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class MineArea extends JPanel implements ActionListener, MouseListener {
	JButton reStart;
	Block[][] block;
	BlockView[][] blockView;
	LayMines lay;
	int row, colum, mineCount, markMount;// 雷区的行数、列数以及地雷个数和用户给出的标记数
	ImageIcon mark;
	int grade; // 初、中、高级
	JPanel pCenter, pNorth;
	JTextField showTime, showMarkedMineCount; // 显示用时以及标记数
	Timer time; // 计时器
	int spendTime = 0;
	Record record;
	JDialog lose = new JDialog();
	JPanel panel;
	JLabel str;	
	JButton restart1 = new JButton("重新开始");
	JButton	cancel = new JButton("取消");

	public MineArea(int row, int colum, int mineCount, int grade) {
		reStart = new JButton("重新开始");
		mark = new ImageIcon("mark.png"); // 探雷标记
		time = new Timer(1000, this);
		showTime = new JTextField(5);
		showMarkedMineCount = new JTextField(5);
		showTime.setHorizontalAlignment(JTextField.CENTER);
		showTime.setFont(new Font("Arial", Font.BOLD, 16));
		showTime.setDisabledTextColor(java.awt.Color.black); // 设置字体颜色
		showTime.setBorder(javax.swing.BorderFactory.createLineBorder(java.awt.Color.gray));// 设置边框颜色
		showTime.setEnabled(false);// 设置不能响应用户输入
		showMarkedMineCount.setHorizontalAlignment(JTextField.CENTER);
		showMarkedMineCount.setFont(new Font("Arial", Font.BOLD, 16));
		showMarkedMineCount.setDisabledTextColor(java.awt.Color.black);
		showMarkedMineCount.setBorder(javax.swing.BorderFactory.createLineBorder(java.awt.Color.gray));
		showMarkedMineCount.setEnabled(false);
		pCenter = new JPanel();
		pNorth = new JPanel();
		lay = new LayMines();
		initMineArea(row, colum, mineCount, grade); // 初始化雷区,见下面的LayMines()
		reStart.addActionListener(this);
		pNorth.add(showMarkedMineCount);
		pNorth.add(reStart);
		pNorth.add(showTime);
		setLayout(new BorderLayout());
		add(pNorth, BorderLayout.NORTH);
		add(pCenter, BorderLayout.CENTER);
	}

	public void initMineArea(int row, int colum, int mineCount, int grade) {
		pCenter.removeAll();
		spendTime = 0;
		markMount = mineCount;
		this.row = row;
		this.colum = colum;
		this.mineCount = mineCount;
		this.grade = grade;
		block = new Block[row][colum];
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < colum; j++)
				block[i][j] = new Block();
		}
		//lay.layMinesForBlock(block, mineCount);
		blockView = new BlockView[row][colum];
		pCenter.setLayout(new GridLayout(row, colum));
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < colum; j++) {
				blockView[i][j] = new BlockView();
				//blockView[i][j].giveView(block[i][j]); // 给block[i][j]提供视图
				pCenter.add(blockView[i][j]);
				blockView[i][j].getBlockCover().addActionListener(this);
				blockView[i][j].getBlockCover().addMouseListener(this);
				blockView[i][j].seeBlockCover();
				blockView[i][j].getBlockCover().setEnabled(true);
				blockView[i][j].getBlockCover().setIcon(null);
			}
		}
		showMarkedMineCount.setText("" + markMount);
		validate();
	}

	public void initMine(int m, int n){
		lay.layMinesForBlock(block, mineCount, m, n);
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < colum; j++) {
				blockView[i][j].giveView(block[i][j]);
			}
		}
	}
	
	public void setRow(int row) {
		this.row = row;
	}

	public void setColum(int colum) {
		this.colum = colum;
	}

	public void setMineCount(int mineCount) {
		this.mineCount = mineCount;
	}

	public void setGrade(int grade) {
		this.grade = grade;
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == restart1) {
			initMineArea(row, colum, mineCount, grade);
			lose.dispose();
			return;
		}
		if (e.getSource() == cancel){
			lose.dispose();
			return;
		}
		int temp = 0;
		if (e.getSource() != reStart && e.getSource() != time) {
			time.start();
			int m = -1, n = -1;
			for (int i = 0; i < row; i++) {
				for (int j = 0; j < colum; j++) {
					if(block[i][j].getIsMine() == true)
						temp = -1;
					if (e.getSource() == blockView[i][j].getBlockCover()) {
						m = i;
						n = j;
					}
				}
			}
			
			if(temp == 0)
				initMine(m, n);
			
			if (block[m][n].getIsMine()) {
				for (int i = 0; i < row; i++) {
					for (int j = 0; j < colum; j++) {
						blockView[i][j].getBlockCover().setEnabled(false);
						if (block[i][j].getIsMine())
							blockView[i][j].seeBlockNameOrIcon();
					}
				}
				panel = new JPanel();
				panel.setLayout(new FlowLayout());
				str = new JLabel("你输了哦!", JLabel.CENTER);
				//restart1 = new JButton("重新开始");
				restart1.addActionListener(this);
				//cancel = new JButton("取消");
				cancel.addActionListener(this);
				lose.setTitle("输了");
				lose.setBounds(300,100,200,150);
				lose.setResizable(false);
				lose.setVisible(false);
				lose.setModal(true);
				time.stop();
				spendTime = 0;
				markMount = mineCount;
				lose.add(str, BorderLayout.CENTER);
				panel.add(restart1);
				panel.add(cancel);
				lose.add(panel, BorderLayout.SOUTH);
				lose.setVisible(true);
			} else {
				show(m, n); // 见本类后面的show方法
			}
		}
		if (e.getSource() == reStart) {
			initMineArea(row, colum, mineCount, grade);
		}
		if (e.getSource() == time) {
			spendTime++;
			showTime.setText("" + spendTime);
		}
		inquireWin();
	}

	public void show(int m, int n) {
		if (block[m][n].getAroundMineNumber() > 0 && block[m][n].getIsOpen() == false) {
			blockView[m][n].seeBlockNameOrIcon();
			block[m][n].setIsOpen(true);
			return;
		} else if (block[m][n].getAroundMineNumber() == 0 && block[m][n].getIsOpen() == false) {
			blockView[m][n].seeBlockNameOrIcon();
			block[m][n].setIsOpen(true);
			for (int k = Math.max(m - 1, 0); k <= Math.min(m + 1, row - 1); k++) {
				for (int t = Math.max(n - 1, 0); t <= Math.min(n + 1, colum - 1); t++)
					show(k, t);
			}
		}
	}

	public void mousePressed(MouseEvent e) {
		JButton source = (JButton) e.getSource();
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < colum; j++) {
				if (e.getModifiers() == InputEvent.BUTTON3_MASK && source == blockView[i][j].getBlockCover()) {
					if (block[i][j].getIsMark()) {
						source.setIcon(null);
						block[i][j].setIsMark(false);
						markMount = markMount + 1;
						showMarkedMineCount.setText("" + markMount);
					} else {
						source.setIcon(mark);
						block[i][j].setIsMark(true);
						markMount = markMount - 1;
						showMarkedMineCount.setText("" + markMount);
					}
				}
			}
		}
	}

	public void inquireWin() {
		int number = 0;
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < colum; j++) {
				if (block[i][j].getIsOpen() == false)
					number++;
			}
		}
		if (number == mineCount) {
			time.stop();
			record = new Record();
			switch (grade) {
			case 1:
				record.setGrade("初级");
				break;
			case 2:
				record.setGrade("中级");
				break;
			case 3:
				record.setGrade("高级");
				break;
			}
			record.setTime(spendTime);
			record.setVisible(true);
		}

	}

	public void mouseReleased(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mouseClicked(MouseEvent e) {
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Copyright © 2010-2022 mfbz.cn 版权所有 |关于我们| 联系方式|豫ICP备15888888号