源文件: 大学生Java课程设计——挖地雷游戏设计报告源文件-其它文档类资源-CSDN文库
目 录
摘 要. 1
一、引言. 2
1.设计任务和基本要求. 2
二、报告正文. 3
1.需求分析. 3
2.概要分析. 6
3.详细分析. 8
4.调试分析. 32
三、总结. 33
四、课程感言. 35
5、 参考文献. 36
附件. 37
在现代,高科技的飞跃发展,人们工作习惯的改变,特别是电脑的大量普及,人们生活节奏越来越快,--些有趣的桌面游戏已经成为人们在使用计算机进行工作或学习之余休闲娱乐的首选,而扫雷游戏是人们最熟悉的小游戏之一-。 扫雷游戏起源于1973年的方块游戏。 1992年微软发布的Windows 3.1中加入该游戏,从此风靡全世界。 玩扫雷游戏,可以锻炼观察和推理能力,培养细心和耐心。 游戏目标是找出所有雷,触雷则输,点击表情重新开始。 由于上手简单、老少皆宜,从而家喻户晓,风靡世界。
为此,我设计了一款简单的扫雷JAVA游戏程序,以便更好的满足广大电脑工作者闲暇之余的消遣,并且也让我更好地学习编程技术。
关键词:扫雷、JAVA游戏、编程
源文件: 大学生Java课程设计——挖地雷游戏设计报告源文件-其它文档类资源-CSDN文库
挖地雷游戏设计报告
1. 设计任务和基本要求
要求:模拟windows系统中的挖地雷游戏,采用Java开发类似的游戏。
本程序实现的主要功能有:
(1)用户可以选择简单、普通、困难三个难度级别;
(2)具有计数功能,即显示用户完成所花费的时间;
(3)具有重新开始的功能,点击笑脸图案即可重新开始游戏。
程序的总体的系统如图1所示:
图 1 系统组成框图
二、正文:
1.需求分析
1.1可行性分析
扫雷游戏是一款玩法简单的小游戏,无论是孩子还是老人,都可以在休
息的时候玩上一局从而轻松而有效地舒缓压力。而且真正的扫雷高手还可以通过竞速(在最短的时间内完成扫雷)来感受乐趣。
(1)投资可行性:扫雷游戏所占内存少,可以搭载在任意平台,有投资的价值。
(2)财务可行性:从受益者的角度来看,不需太多的经费就能开发这款扫雷小游戏。
(3)组织可行性:制度可行的项目规划,做好人员的配给,保持组员间的良好沟通,定期开会讨论,确保项目能够准时交付。
(4) 经济可行性:可以给开发的企业创造效益,给社会增加工作岗位,并且提高人们的生活质量。
(5) 法律可行性:任何的产品都需要确保它没有触犯法律才能开始设计。但是扫雷游戏并不会触犯任何法律,而且不会和企业之间发生冲突。游戏的开发不会侵犯任何人的利益,也不会违法。
(6) 技术可行性:扫雷游戏的功能简单,只需稍有经验的开发人员就能轻易开发出来,所以技术方面不是太大的问题,主要需要明白扫雷的玩法才能更好的设计与实现该游戏。
1.2扫雷游戏功能描述
游戏界面可以分为三个模块:菜单栏,游戏数据显示区域以及雷区。菜单栏为游戏难度的选择。游戏数据显示区域包括地雷计数区、计时区和重新开始按钮。雷区就是玩家的游戏区域,当玩家在游戏过程中用鼠标点击某一模块,系统会自动做出相应的响应,这就是游戏中的鼠标事件。
当玩家点开第一个格子时雷区开始布雷同时计时开始。然后会在该格子上显示周围8个格子的雷数(如果没有雷则自动点开周围的格子),玩家就需要通过这些数字来判断雷的位置,将是雷的格子标记为小红旗。
若玩家认为已标记的格子时错误的,可以再次右击该格子来取消标记。当某一格子被标记时,对于该格子的单击操作是无效的(防止玩家误点击导致游戏失败)。
如果玩家将某一格周围8个格子中的雷标记了出来,右键点击该格子会自动将周围的格子点击一遍,这样可以简化玩家的操作。
当玩家将全部的地雷标记出来并且其他的格子点开时,游戏结束。但是如果玩家不小心点到了地雷就会游戏失败,系统自动显示出所有的地雷。如果玩家标识的地雷数量超过了该难度下规定的雷数,计数区会以负数显示超出的雷数并且游戏不会结束。
1.3扫雷游戏功能需求
本次扫雷设计需要实现的主要功能有:
(1)玩家可以选择合适的游戏难度
点击游戏界面会出现“简单”、“普通”、“困难”三种选项。在玩家选择难度之后游戏将会改变界面尺寸,并改变雷的总数。
(2)进行扫雷游戏
游戏界面包括游戏数据显示区域以及雷区,当玩家点击雷区中的任意一格的时候游戏开始,雷区开始随机布雷,玩家需要在最短的时间内找出雷区中所有的地雷,并加以标识。
扫雷的基本操作包括鼠标左键单击和右键单击两种。其中左键用来点开玩家认为不是雷的格子,右键标记玩家认为是雷的格子。
左键单击:玩家在判断出该格子下没有雷时单击该格子,可以将该格子点开。如果该格子周围有雷就会显示周围雷的数目(由于周围最多只有8个格子,所以最多只能显示8);如果格子上什么也不显示(也就是为空),系统就会自动调用递归的方法打开附近的格子。如果点到了有地雷的格子,那么游戏结束,系统显示所有雷的位置。
右键单击:玩家可以通过使用鼠标右键单击来标记自己认为是雷的格子,通过标记可以有效的提高扫雷游戏的效率。再次右击该格子可以问号标记,且格子被标记的时候鼠标单击无效(防止玩家误操作导致游戏失败)。
(3)游戏计时
当点击雷区任意一个格子的时候雷区开始布雷同时计时开始,计时标准是一秒增加1。如果游戏失败时,则计时停止。如果玩家开始了新游戏,计时也会重新开始。
(4)标记地雷
当玩家认为格子下有雷时可以右击格子来标记该格子,被标记的格子显示小红旗。再次右击可以取消标记,且格子被标记的时候鼠标单击无效(防止玩家误操作导致游戏失败)。如果玩家标识的地雷的数量超过了该难度下规定的雷数,计数区会以负数来显示超过的雷数并且游戏不会结束。
(5)退出
点击关闭按钮可以结束游戏。
1.4 扫雷游戏界面需求
(1)游戏菜单
玩家有3项可以选择,玩家在点击简单时游戏界面的尺寸会变为初级的大小,雷区重新初始化,变为一共9*9的格子,其中有10颗是地雷;普通一共有16*16个格子,其中有40颗是地雷;困难一共有16*30个格子,其中有99颗是雷。
玩家通过点击鼠标右键来标记自己认为是雷的格子,通过标记可以有效的提高扫雷游戏的效率,再次点击右键可以取消标记。
(2)游戏区域
游戏区域由扫雷信息统计区和雷区组成,其中扫雷信息统计区又分为计数区、计时区、重新开始按钮。
雷区的雷数,每个难度对应的雷数都不同,简单、普通、困难分别对应10、40、99个雷。
计数区初始显示的雷数由难度而定,每次标记地雷数均减1,如果玩家标记的地雷的数量超过了该难度下规定的雷数,计数区会以负数显示超过的雷数。
当玩家点开第一个格子雷区开始布雷同时计时开始,一秒加1,直到游戏胜利或者游戏失败的时候停止计时。
1.5扫雷游戏功能模块
游戏在功能上分为6个模块:
(1) 游戏界面
(2) 布雷
(3) 鼠标事件
(4) 地雷判断
(5) 游戏胜利(结束)
(6) 游戏失败(结束)
2.概要分析
2.1设计构想
如今世界上很多人都在使用Windows操作系统,所以人们也对系统自带的小游戏了如指掌。扫雷游戏的玩法简单,只要玩家进行一定的判断就可以轻松的游戏,所以玩扫雷的时候可以很轻松的玩。除了游戏本身带给人们的乐趣以外,游戏的玩法也在锻炼玩家的思维,如今大部分人都是依赖脑力劳动,这就可以通过在闲暇的时候玩玩扫雷来锻炼一下自己。所以就可以理解,为什么在各种电子产品上都搭载这个小游戏了。
虽然游戏比较简单,但是还是需要熟悉一下规则。
玩家需要在最短的时间内找出雷区中所有的地雷,并加以标识,其他没有雷的格子全部点开后游戏胜利。但是如果点到了地雷则游戏失败。
游戏的操作很简单,当玩家用鼠标左键点击自己认为不是地雷的格子会点开该格子,用鼠标右键点击格子会标记该格子,再次右键点击可以标记问号。玩家可以通过雷区中被点开的格子上显示的数字来判断该格子周围8个格子所隐藏的地雷,例如:点开的格子显示数字“2”,则表示该格子周边的8个格子里隐藏着2颗地雷。
如果点开的格子下没有雷而且周围8个格子里也没有雷,则系统会自动点开那8个格子,然后递归判断这些格子周围有没有雷。
本次的扫雷游戏设计,需要编写7个Java类:GameWin.java类、GameUtil.java类、GameSelect.java类、BottomNum.java类、BottomRay.java类、MapBottom.java类和Maptop.java类。
(1) GameWin.java
GameWin类是游戏的入口,用来初始化游戏资源,比如界面尺寸和雷数等。同时也负责难度的转换。
(2) GameUtil.java
GameUtil类是工具类、存放静态参数、工具方法类。
(3) GameSelect.java
GameSelect类难度选择类。
(4) BottomNum.java
BottomNum类底层数字类。
(5) BottomRay.java
BottomRay类是初始化地雷类。
(6) MapBottom.java
MapBottom类是底层地图、绘制游戏相关组件类。
(7) Maptop.java
Maptop类是显示顶层地图、绘制顶层组件、判断逻辑类。
2.2算法思想
(1)随机布雷
扫雷游戏要求在雷区随机布雷,雷数不能太多,这样就没法很好的判断周围是否有雷了;但也不能太少,这样会出现点一下就会点开一大片的空白区域。使用java自带的Math.random()方法产生随机数,经过计算后得到将随机数转换成一个整数,这个整数就是雷的位置的角标。游戏的目标是在将所有的地雷标记出来,并将其它不是雷的格子点开。
(2)计算方格周围雷数
当没有雷的格子被点击后,会显示该格子周围8个格子里所有的雷数,玩家通过这个数字就可以判断出雷的位置,所以周围雷数的计算也很重要。
3.详细分析
3.1窗口绘制
新建GameWin类
import javax.swing.*;
public class GameWin extends JFrame {
void launch(){
this.setVisible(true);
this.setSize(500,500);
this.setLocationRelativeTo(null);
this.setTitle("扫雷游戏");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
GameWin gameWin = new GameWin();
gameWin.launch();
}
}
运行效果如图2所示
图2 窗口绘制
3.2雷区绘制
新建MapBottom类,用于底层地图以及绘制游戏相关组件
import java.awt.*;
/**
* 底层地图
* 绘制游戏相关组件
*/
public class MapBottom {
//绘制方法
void paintSelf(Graphics g){
for (int i = 0; i < 500; i=i+50) {
g.setColor(Color.red);
g.drawLine(0,i,500,i);
g.drawLine(i,0,i,500);
}
}
}
并在GameWin类中引用绘制方法
public void paint(Graphics g) {
mapBottom.paintSelf(g);
}
运行效果如图3所示
图3 雷区绘制
3.3界面规划
新建GameUtil类,工具类,用于存放静态参数,工具方法。
/**
* 工具类
* 存放静态参数
* 工具方法
*/
public class GameUtil {
//地图的宽
static int MAP_W = 11;
//地图的高
static int MAP_H = 11;
//雷区偏移量
static int OFFSET = 45;
//格子边长
static int SQUARE_LENGTH = 50;
}
在GameWin类中定义变量wigth、height。
int wigth = 2 * GameUtil.OFFSET + GameUtil.MAP_W *
GameUtil.SQUARE_LENGTH;
int height = 4 * GameUtil.OFFSET + GameUtil.MAP_H *
GameUtil.SQUARE_LENGTH;
在MapBottom类中用for循环语句进行界面规划
//画竖线
for (int i = 0; i <= GameUtil.MAP_W; i++) {
g.drawLine(GameUtil.OFFSET + i * GameUtil.SQUARE_LENGTH,
3*GameUtil.OFFSET,
GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH,
3*GameUtil.OFFSET+GameUtil.MAP_H*GameUtil.SQUARE_LENGTH);
}
//画横线
for (int i = 0; i <=GameUtil.MAP_H; i++){
g.drawLine(GameUtil.OFFSET,
3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH,
GameUtil.OFFSET+GameUtil.MAP_W*GameUtil.SQUARE_LENGTH,
3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH);
}
运行效果如图4所示
图4 界面规划
3.4 底层地图
在MapBottom类中运用双层for循环绘制雷
for (int i = 1; i <= GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
g.drawImage(GameUtil.lei,
GameUtil.OFFSET+(i-1)*GameUtil.SQUARE_LENGTH+1,
GameUtil.OFFSET*3+(j-1)*GameUtil.SQUARE_LENGTH+1,
GameUtil.SQUARE_LENGTH-2,
GameUtil.SQUARE_LENGTH-2,
null);
}
}
在GameUtil类中载入雷的图片
//载入图片
static Image lei
= Toolkit.getDefaultToolkit().getImage("imgs/lei.png");
运行效果如图5所示
图5 底层地图
3.5地雷生成
新建BottomRay类,随机初始化地雷。
/**
* 初始化地雷
*/
public class BottomRay {
//存放坐标
int[] rays = new int[GameUtil.RAY_MAX*2];
//地雷坐标
int x,y;
{
for (int i = 0; i < GameUtil.RAY_MAX*2 ; i=i+2) {
x= (int) (Math.random()*GameUtil.MAP_W +1);//1-12
y= (int) (Math.random()*GameUtil.MAP_H +1);//1-12
rays[i]=x;
rays[i+1]=y;
}
for (int i = 0; i < GameUtil.RAY_MAX*2; i=i+2) {
GameUtil.DATA_BOTTOM[rays[i]][rays[i+1]]=-1;
}
}
}
运行效果如图6所示
图6 地雷生成
3.6 数字生成
3.6.1新建BottomNum类,使用四层for循环语句遍历每个格子周围的八个格子。
/**
* 底层数字类
*/
public class BottomNum {
{
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
if(GameUtil.DATA_BOTTOM[i][j]==-1){
for (int k = i-1; k <=i+1 ; k++) {
for (int l = j-1; l <=j+1 ; l++) {
if(GameUtil.DATA_BOTTOM[k][l]>=0){
GameUtil.DATA_BOTTOM[k][l]++;
}
}
}
}
}
}
}
}
3.6.2在GameUtil类中载入数字1-8的图片
static Image[] images = new Image[9];
static {
for (int i = 1; i <=8 ; i++) {
images[i] =
Toolkit.getDefaultToolkit().getImage("imgs/num/"+i+".png");
}
}
3.6.3在MapBottom类中运用双层for循环绘制数字
for (int i = 1; i <= GameUtil.MAP_W ; i++) {
for (int j = 1; j <= GameUtil.MAP_H; j++) {
if (GameUtil.DATA_BOTTOM[i][j] >=0) {
g.drawImage(GameUtil.images[GameUtil.DATA_BOTTOM[i][j]],
GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 15,
GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 5,
null);
}
}
}
运行效果如图7所示:
图7 数字生成
3.7顶部绘制
新建MapTop类用于绘制顶层组件和判断逻辑
import java.awt.*;
/**
* 顶层地图类
* 绘制顶层组件
* 判断逻辑
*/
public class MapTop {
//绘制方法
void paintSelf(Graphics g){
for (int i = 1; i <= GameUtil.MAP_W ; i++) {
for (int j = 1; j <= GameUtil.MAP_H; j++) {
//覆盖
if (GameUtil.DATA_TOP[i][j] == 0) {
g.drawImage(GameUtil.top,
GameUtil.OFFSET + (i - 1) *
GameUtil.SQUARE_LENGTH + 1,
GameUtil.OFFSET * 3 + (j - 1) *
GameUtil.SQUARE_LENGTH + 1,
GameUtil.SQUARE_LENGTH - 2,
GameUtil.SQUARE_LENGTH - 2,
null);
}
//插旗
if (GameUtil.DATA_TOP[i][j] == 1) {
g.drawImage(GameUtil.flag,
GameUtil.OFFSET + (i - 1) *
GameUtil.SQUARE_LENGTH + 1,
GameUtil.OFFSET * 3 + (j - 1) *
GameUtil.SQUARE_LENGTH + 1,
GameUtil.SQUARE_LENGTH - 2,
GameUtil.SQUARE_LENGTH - 2,
null);
}
//差错旗
if (GameUtil.DATA_TOP[i][j] == 2) {
g.drawImage(GameUtil.noflag,
GameUtil.OFFSET + (i - 1) *
GameUtil.SQUARE_LENGTH + 1,
GameUtil.OFFSET * 3 + (j - 1) *
GameUtil.SQUARE_LENGTH + 1,
GameUtil.SQUARE_LENGTH - 2,
GameUtil.SQUARE_LENGTH - 2,
null);
}
}
}
}
}
运行效果如图8所示
图8 顶部绘制
3.8 左键递归翻开
3.8.1 在GameWin类添加鼠标事件
//鼠标事件
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if(e.getButton()==1){
GameUtil.MOUSE_X = e.getX();
GameUtil.MOUSE_Y = e.getY();
GameUtil.LEFT = true;
}
if(e.getButton()==3) {
GameUtil.MOUSE_X = e.getX();
GameUtil.MOUSE_Y = e.getY();
GameUtil.RIGHT = true;
}
3.8.2 在Maptop类添加logic判断逻辑方法以及递归打开方格
//判断逻辑
void logic(){
temp_x=0;
temp_y=0;
if(GameUtil.MOUSE_X>GameUtil.OFFSET &&
GameUtil.MOUSE_Y>3*GameUtil.OFFSET){
temp_x = (GameUtil.MOUSE_X –
GameUtil.OFFSET)/GameUtil.SQUARE_LENGTH+1;
temp_y = (GameUtil.MOUSE_Y - GameUtil.OFFSET *
3)/GameUtil.SQUARE_LENGTH+1;
}
if(temp_x>=1 && temp_x<=GameUtil.MAP_W
&& temp_y>=1 && temp_y<=GameUtil.MAP_H){
if(GameUtil.LEFT){
if(GameUtil.DATA_TOP[temp_x][temp_y]==0){
GameUtil.DATA_TOP[temp_x][temp_y]=-1;
}
spaceOpen(temp_x,temp_y);
GameUtil.LEFT=false;
}
//递归打开空格
void spaceOpen(int x,int y){
if(GameUtil.DATA_BOTTOM[x][y]==0){
for (int i = x-1; i <=x+1 ; i++) {
for (int j = y-1; j <=y+1 ; j++) {
//覆盖,才递归
if(GameUtil.DATA_TOP[i][j]!=-1){
GameUtil.DATA_TOP[i][j]=-1;
//必须在雷区当中
if(i>=1&&j>=1&&i<=GameUtil.MAP_W&&j<=GameUtil.MAP_H){
spaceOpen(i,j);
}
}
}
}
}
运行效果如图9所示:
图9 左键递归翻开
3.9 右键插旗、问号、翻开
在MapTop类中加入右键的判断语句
if(temp_x>=1 && temp_x<=GameUtil.MAP_W
&& temp_y>=1 && temp_y<=GameUtil.MAP_H){
if(GameUtil.RIGHT){
//覆盖则插旗
if(GameUtil.DATA_TOP[temp_x][temp_y]==0){
GameUtil.DATA_TOP[temp_x][temp_y]=1;
}
//插旗则取消
else if(GameUtil.DATA_TOP[temp_x][temp_y]==1){
GameUtil.DATA_TOP[temp_x][temp_y]=2;
}
//问号则取消
else if(GameUtil.DATA_TOP[temp_x][temp_y]==2){
GameUtil.DATA_TOP[temp_x][temp_y]=0;
}
else if(GameUtil.DATA_TOP[temp_x][temp_y]==-1){
numOpen(temp_x,temp_y);
}
GameUtil.RIGHT=false;
}
运行效果如图10所示:
图10 右键插旗、问号、翻开
3.10 失败判定
3.10.1 在MapBottom类中加入游戏状态判断语句
switch (GameUtil.state){
case 0:
g.drawImage(GameUtil.face,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
GameUtil.OFFSET,
null);
break;
case 1:
g.drawImage(GameUtil.win,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
GameUtil.OFFSET,
null);
break;
case 2:
g.drawImage(GameUtil.over,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
GameUtil.OFFSET,
null);
break;
default:
}
3.10.2 在MapTop类中引入失败判定方法
//失败判定 t 表示失败 f 未失败
boolean boom(){
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
if(GameUtil.DATA_BOTTOM[i][j]==-1&&GameUtil.DATA_TOP[i][j]==-1){
GameUtil.state = 2;
seeBoom();
return true;
}
}
}
return false;
}
//失败显示
void seeBoom(){
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
//底层是雷,顶层不是旗,显示
if(GameUtil.DATA_BOTTOM[i][j]==-1&&GameUtil.DATA_TOP[i][j]!=1){
GameUtil.DATA_TOP[i][j]=-1;
}
//底层不是雷,顶层是旗,显示差错旗
if(GameUtil.DATA_BOTTOM[i][j]!=-1&&GameUtil.DATA_TOP[i][j]==1){
GameUtil.DATA_TOP[i][j]=2;
}
}
}
}
运行效果如图11所示:
图11 失败判定
3.11 胜利判定
3.11.1 在MapTop类中引入胜利判定方法
//胜利判断 t 表示胜利 f 未胜利
boolean victory(){
//统计未打开格子数
int count=0;
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
if(GameUtil.DATA_TOP[i][j]!=-1){
count++;
}
}
}
if(count==GameUtil.RAY_MAX){
GameUtil.state=1;
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
//未翻开,变成旗
if(GameUtil.DATA_TOP[i][j]==0){
GameUtil.DATA_TOP[i][j]=1;
}
}
}
return true;
}
return false;
}
运行效果如图12所示:
图12 胜利判定
3.12游戏重置
3.12.1 在MapTop类中引入游戏重置方法
//重置游戏
void reGame(){
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
GameUtil.DATA_TOP[i][j]=0;
}
}
}
3.12.2 在MapBottom类中引入游戏重置方法
//重置游戏
void reGame(){
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
GameUtil.DATA_BOTTOM[i][j]=0;
}
}
bottomRay.newRay();
bottomNum.newNum();
}
3.12.3 在GameWin类中引入游戏重置鼠标事件
//鼠标事件
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
switch (GameUtil.state){
case 0 :
if(e.getButton()==1){
GameUtil.MOUSE_X = e.getX();
GameUtil.MOUSE_Y = e.getY();
GameUtil.LEFT = true;
}
if(e.getButton()==3) {
GameUtil.MOUSE_X = e.getX();
GameUtil.MOUSE_Y = e.getY();
GameUtil.RIGHT = true;
}
case 1 :
case 2 :
if(e.getButton()==1){
if(e.getX()>GameUtil.OFFSET +
GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W/2)
&& e.getX()GameUtil.OFFSET
&& e.getY()
运行效果如图13所示:
图13 游戏重置
3.13 数字添加
3.13.1 在MapBootom类中添加数字绘制
//绘制数字 剩余雷数,倒计时
GameUtil.drawWord(g,""+(GameUtil.RAY_MAX-GameUtil.FLAG_NUM),
GameUtil.OFFSET,
2*GameUtil.OFFSET,30,Color.red);
GameUtil.drawWord(g,""+(GameUtil.END_TIME-GameUtil.START_TIME)/1000,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W-1),
2*GameUtil.OFFSET,30,Color.red);
switch (GameUtil.state){
case 0:
GameUtil.END_TIME=System.currentTimeMillis();
g.drawImage(GameUtil.face,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
GameUtil.OFFSET,
null);
break;
case 1:
g.drawImage(GameUtil.win,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
GameUtil.OFFSET,
null);
break;
case 2:
g.drawImage(GameUtil.over,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
GameUtil.OFFSET,
null);
break;
default:
}
运行效果如图13所示:
图14 数字添加
3.14 难度选择
3.14.1 新建GameSelect类
import java.awt.*;
/**
* 难度选择类
*/
public class GameSelect {
//判断是否点击到难度
boolean hard(){
if(GameUtil.MOUSE_X>100&&GameUtil.MOUSE_X<400){
if(GameUtil.MOUSE_Y>50&&GameUtil.MOUSE_Y<150){
GameUtil.level=1;
GameUtil.state=0;
return true;
}
if(GameUtil.MOUSE_Y>200&&GameUtil.MOUSE_Y<300){
GameUtil.level=2;
GameUtil.state=0;
return true;
}
if(GameUtil.MOUSE_Y>350&&GameUtil.MOUSE_Y<450){
GameUtil.level=3;
GameUtil.state=0;
return true;
}
}
return false;
}
//绘制框图
void paintSelf(Graphics g){
g.setColor(Color.black);
g.drawRect(100,50,300,100);
GameUtil.drawWord(g,"简单",220,100,30,Color.black);
g.drawRect(100,200,300,100);
GameUtil.drawWord(g,"普通",220,250,30,Color.black);
g.drawRect(100,350,300,100);
GameUtil.drawWord(g,"困难",220,400,30,Color.black);
}
void hard(int level){
switch (level){
case 1:
GameUtil.RAY_MAX = 10;
GameUtil.MAP_W = 9;
GameUtil.MAP_H = 9;
break;
case 2:
GameUtil.RAY_MAX = 40;
GameUtil.MAP_W = 16;
GameUtil.MAP_H = 16;
break;
case 3:
GameUtil.RAY_MAX = 99;
GameUtil.MAP_W = 30;
GameUtil.MAP_H = 16;
break;
default:
}
}
}
3.14.2 在GameWin类中添加绘制语句
@Override
public void paint(Graphics g) {
if(GameUtil.state==3){
g.setColor(Color.white);
g.fillRect(0,0,500,500);
gameSelect.paintSelf(g);
}else {
offScreenImage = this.createImage(wigth, height);
Graphics gImage = offScreenImage.getGraphics();
//设置背景颜色
gImage.setColor(Color.orange);
gImage.fillRect(0, 0, wigth, height);
mapBottom.paintSelf(gImage);
mapTop.paintSelf(gImage);
g.drawImage(offScreenImage, 0, 0, null);
}
}
运行效果如图15所示:
图15 难度选择
4.调试分析
4.1 地雷重合问题
4.1.1在BottomRay类中生成雷时判断
for (int i = 0; i < GameUtil.RAY_MAX*2 ; i=i+2) {
x= (int) (Math.random()*GameUtil.MAP_W +1);//1-12
y= (int) (Math.random()*GameUtil.MAP_H +1);//1-12
//判断坐标是否存在
for (int j = 0; j < i ; j=j+2) {
if(x==rays[j] && y==rays[j+1]){
i=i-2;
isPlace = false;
break;
}
}
//将坐标放入数组
if(isPlace){
rays[i]=x;
rays[i+1]=y;
}
isPlace = true;
}
4.2 闪烁问题
4.2.1 在GameWin类中用双缓存技术解决闪烁问题
while (true){
repaint();
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三、总结:
1.运行结果
简单、普通、困难类型运行效果分别如图16、17、18所示:
图16 简单类型
图17 普通类型
图18 困难类型
四、课程感言:
此次课程设计的题目为扫雷游戏,通过课程设计,进一步巩固、加深我们所学专业课程《JAVA面向对象程序设计》的基本理论知识,理论联系实际,进一步培养我们的综合分析问题,解决问题的能力;全面考核我们所掌握的基本理论知识及其实际业务能力,从而达到提高学生素质的最终目的;利用所学知识,开发小型应用系统,掌握运用JAVA语言编写调试应用系统程序,训练独立开发应用系统,进行数据处理的综合能力;对于给定的设计题目,如何进行分析,理清思路,并给出相应的数学模型;熟练掌握JAVA语言的GUI设计、线程技术,灵活运用各种类库,为今后从事实际工作打下坚实的基础。通过该课程设计,全面系统的理解了JAVA语言的一般原理和基本实现方法。把死板的课本知识变得生动有趣,激发了学习的积极性。把学过的JAVA的知识强化,能够把课堂上学的知识通过自己设计的程序表示出来,加深了对理论知识的理解。我在网上查询了许多与扫雷游戏程序的相关资料,终于完成了扫雷游戏的设计。程序基本实现了该课程设计的基本要求。在设计的过程中了解到自己的知识还是比较有限,更肯定了自己再以后学习编程的道路上的坚定不移,同时也让我懂得组合作的重要性。但由于只学习了一个学期的Java 语言,自己水平有限,使得程序还是有些不完善的地方。回顾起此次Java课程设计,至今我仍感慨颇多,的确,从拿到题目的开始,从理论到实践,在整整两个星期的日子里,可以说是苦多于甜,但是可以学到很多很多的东西,同时不仅可以巩固了以前所学的知识,而且学到了很多在书本上所没有学到过的知识。通过这次课程设计使我懂得了理论与实践相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正掌握这门技术,也提高了自己的独立思考的能力。在设计的过程遇到问题,可以说得上是困难重重,这毕竟第一次做的,难免会遇到各种各样的问题,同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,掌握得不够牢固,通过这次之后,一定把以前所学的知识重新温故,提升自己的能力。
五、参考文献
[1] 耿祥义.Java大学实用教程[M].北京:清华大学出版社,2009.
[2] 冯锋,王运坚.Visual Basic 程序设计基础教程[M].西安:电子工业出版社,1999。
[3] 王鹏.JavaSwing图形界面开发与案例详解[M].北京:清华大学出版社,2008。
[4] 李怀明.Visual Basic 6.0 中文版 参考详解[M].北京:清华大学出版社,1999。
[5] 丁振凡.Java语言实验教程[M].北京:北京邮电大学出版社,2005。
[6] 郑莉.Java语言程序设计[M].北京:清华大学出版社,2006。
[7] 伍俊良.VB课程设计与系统开发案例[M].北京:清华大学出版社,2002。
[8] 孙全党,王吴迪,赵枫朝.Java程序设计应用教程[M].北京:电子工业出版社,2006。
[9] 雷之宇.Java项目开发实践-网络篇[M].北京:中国铁道出版社,2005。
[10] 赵玉阳.Java从入门到精通[M].北京:清华大学出版社,2006。
附件:
程序完整代码
1.MapTop类
import java.awt.*;
/**
* 顶层地图类
* 绘制顶层组件
* 判断逻辑
*/
public class MapTop {
//格子位置
int temp_x;
int temp_y;
//重置游戏
void reGame(){
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
GameUtil.DATA_TOP[i][j]=0;
}
}
}
//判断逻辑
void logic(){
temp_x=0;
temp_y=0;
if(GameUtil.MOUSE_X>GameUtil.OFFSET &&
GameUtil.MOUSE_Y>3*GameUtil.OFFSET){
temp_x = (GameUtil.MOUSE_X –
GameUtil.OFFSET)/GameUtil.SQUARE_LENGTH+1;
temp_y = (GameUtil.MOUSE_Y - GameUtil.OFFSET *
3)/GameUtil.SQUARE_LENGTH+1;
}
if(temp_x>=1 && temp_x<=GameUtil.MAP_W
&& temp_y>=1 && temp_y<=GameUtil.MAP_H){
if(GameUtil.LEFT){
//覆盖,则翻开
if(GameUtil.DATA_TOP[temp_x][temp_y]==0){
GameUtil.DATA_TOP[temp_x][temp_y]=-1;
}
spaceOpen(temp_x,temp_y);
GameUtil.LEFT=false;
}
if(GameUtil.RIGHT){
//覆盖则插旗
if(GameUtil.DATA_TOP[temp_x][temp_y]==0){
GameUtil.DATA_TOP[temp_x][temp_y]=1;
GameUtil.FLAG_NUM++;
}
//插旗则问号
else if(GameUtil.DATA_TOP[temp_x][temp_y]==1){
GameUtil.DATA_TOP[temp_x][temp_y]=2;
GameUtil.FLAG_NUM--;
}
//问号则取消
else if(GameUtil.DATA_TOP[temp_x][temp_y]==2){
GameUtil.DATA_TOP[temp_x][temp_y]=0;
}
else if(GameUtil.DATA_TOP[temp_x][temp_y]==-1){
numOpen(temp_x,temp_y);
}
GameUtil.RIGHT=false;
}
}
boom();
victory();
}
//数字翻开
void numOpen(int x,int y){
//记录旗数
int count=0;
if(GameUtil.DATA_BOTTOM[x][y]>0){
for (int i = x-1; i <=x+1 ; i++) {
for (int j = y-1; j <=y+1 ; j++) {
if(GameUtil.DATA_TOP[i][j]==1){
count++;
}
}
}
if(count==GameUtil.DATA_BOTTOM[x][y]){
for (int i = x-1; i <=x+1 ; i++) {
for (int j = y-1; j <=y+1 ; j++) {
if(GameUtil.DATA_TOP[i][j]!=1){
GameUtil.DATA_TOP[i][j]=-1;
}
//必须在雷区当中
if(i>=1&&j>=1&&i<=GameUtil.MAP_W&&j<=GameUtil.MAP_H){
spaceOpen(i,j);
}
}
}
}
}
}
//失败判定 t 表示失败 f 未失败
boolean boom(){
if(GameUtil.FLAG_NUM==GameUtil.RAY_MAX){
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
if(GameUtil.DATA_TOP[i][j]==0){
GameUtil.DATA_TOP[i][j]=-1;
}
}
}
}
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
if(GameUtil.DATA_BOTTOM[i][j]==-1&&GameUtil.DATA_TOP[i][j]==-1){
GameUtil.state = 2;
seeBoom();
return true;
}
}
}
return false;
}
//失败显示
void seeBoom(){
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
//底层是雷,顶层不是旗,显示
if(GameUtil.DATA_BOTTOM[i][j]==-1&&GameUtil.DATA_TOP[i][j]!=1){
GameUtil.DATA_TOP[i][j]=-1;
}
//底层不是雷,顶层是旗,显示差错旗
if(GameUtil.DATA_BOTTOM[i][j]!=-1&&GameUtil.DATA_TOP[i][j]==1){
GameUtil.DATA_TOP[i][j]=2;
}
}
}
}
//胜利判断 t 表示胜利 f 未胜利
boolean victory(){
//统计未打开格子数
int count=0;
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
if(GameUtil.DATA_TOP[i][j]!=-1){
count++;
}
}
}
if(count==GameUtil.RAY_MAX){
GameUtil.state=1;
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
//未翻开,变成旗
if(GameUtil.DATA_TOP[i][j]==0){
GameUtil.DATA_TOP[i][j]=1;
}
}
}
return true;
}
return false;
}
//打开空格
void spaceOpen(int x,int y){
if(GameUtil.DATA_BOTTOM[x][y]==0){
for (int i = x-1; i <=x+1 ; i++) {
for (int j = y-1; j <=y+1 ; j++) {
//覆盖,才递归
if(GameUtil.DATA_TOP[i][j]!=-1){
if(GameUtil.DATA_TOP[i][j]==1){GameUtil.FLAG_NUM--;}
GameUtil.DATA_TOP[i][j]=-1;
//必须在雷区当中
if(i>=1&&j>=1&&i<=GameUtil.MAP_W&&j<=GameUtil.MAP_H){
spaceOpen(i,j);
}
}
}
}
}
}
//绘制方法
void paintSelf(Graphics g){
logic();
for (int i = 1; i <= GameUtil.MAP_W ; i++) {
for (int j = 1; j <= GameUtil.MAP_H; j++) {
//覆盖
if (GameUtil.DATA_TOP[i][j] == 0) {
g.drawImage(GameUtil.top,
GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1,
GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1,
GameUtil.SQUARE_LENGTH - 2,
GameUtil.SQUARE_LENGTH - 2,
null);
}
//插旗
if (GameUtil.DATA_TOP[i][j] == 1) {
g.drawImage(GameUtil.flag,
GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1,
GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1,
GameUtil.SQUARE_LENGTH - 2,
GameUtil.SQUARE_LENGTH - 2,
null);
}
//差错旗
if (GameUtil.DATA_TOP[i][j] == 2) {
g.drawImage(GameUtil.noflag,
GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1,
GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1,
GameUtil.SQUARE_LENGTH - 2,
GameUtil.SQUARE_LENGTH - 2,
null);
}
}
}
}
}
2.MapBottom类
import java.awt.*;
/**
* 底层地图
* 绘制游戏相关组件
*/
public class MapBottom {
BottomRay bottomRay = new BottomRay();
BottomNum bottomNum = new BottomNum();
{
bottomRay.newRay();
bottomNum.newNum();
}
//重置游戏
void reGame(){
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
GameUtil.DATA_BOTTOM[i][j]=0;
}
}
bottomRay.newRay();
bottomNum.newNum();
}
//绘制方法
void paintSelf(Graphics g){
g.setColor(Color.red);
//画竖线
for (int i = 0; i <= GameUtil.MAP_W; i++) {
g.drawLine(GameUtil.OFFSET + i * GameUtil.SQUARE_LENGTH,
3*GameUtil.OFFSET,
GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH,
3*GameUtil.OFFSET+GameUtil.MAP_H*GameUtil.SQUARE_LENGTH);
}
//画横线
for (int i = 0; i <=GameUtil.MAP_H; i++){
g.drawLine(GameUtil.OFFSET,
3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH,
GameUtil.OFFSET+GameUtil.MAP_W*GameUtil.SQUARE_LENGTH,
3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH);
}
for (int i = 1; i <= GameUtil.MAP_W ; i++) {
for (int j = 1; j <= GameUtil.MAP_H; j++) {
//雷
if (GameUtil.DATA_BOTTOM[i][j] == -1) {
g.drawImage(GameUtil.lei,
GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1,
GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1,
GameUtil.SQUARE_LENGTH - 2,
GameUtil.SQUARE_LENGTH - 2,
null);
}
//数字
if (GameUtil.DATA_BOTTOM[i][j] >=0) {
g.drawImage(GameUtil.images[GameUtil.DATA_BOTTOM[i][j]],
GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 15,
GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 5,
null);
}
}
}
//绘制数字 剩余雷数,倒计时
GameUtil.drawWord(g,""+(GameUtil.RAY_MAX-GameUtil.FLAG_NUM),
GameUtil.OFFSET,
2*GameUtil.OFFSET,30,Color.red);
GameUtil.drawWord(g,""+(GameUtil.END_TIME-GameUtil.START_TIME)/1000,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W-1),
2*GameUtil.OFFSET,30,Color.red);
switch (GameUtil.state){
case 0:
GameUtil.END_TIME=System.currentTimeMillis();
g.drawImage(GameUtil.face,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
GameUtil.OFFSET,
null);
break;
case 1:
g.drawImage(GameUtil.win,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
GameUtil.OFFSET,
null);
break;
case 2:
g.drawImage(GameUtil.over,
GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2),
GameUtil.OFFSET,
null);
break;
default:
}
}
}
3.BottomNum类
/**
* 底层数字类
*/
public class BottomNum {
void newNum() {
for (int i = 1; i <=GameUtil.MAP_W ; i++) {
for (int j = 1; j <=GameUtil.MAP_H ; j++) {
if(GameUtil.DATA_BOTTOM[i][j]==-1){
for (int k = i-1; k <=i+1 ; k++) {
for (int l = j-1; l <=j+1 ; l++) {
if(GameUtil.DATA_BOTTOM[k][l]>=0){
GameUtil.DATA_BOTTOM[k][l]++;
}
}
}
}
}
}
}
}
4.BottomRay类
/**
* 初始化地雷
*/
public class BottomRay {
//存放坐标
static int[] rays = new int[GameUtil.RAY_MAX*2];
//地雷坐标
int x,y;
//是否放置 T 表示可以放置 F 不可放置
boolean isPlace = true;
//生成雷
void newRay() {
for (int i = 0; i < GameUtil.RAY_MAX*2 ; i=i+2) {
x= (int) (Math.random()*GameUtil.MAP_W +1);//1-12
y= (int) (Math.random()*GameUtil.MAP_H +1);//1-12
//判断坐标是否存在
for (int j = 0; j < i ; j=j+2) {
if(x==rays[j] && y==rays[j+1]){
i=i-2;
isPlace = false;
break;
}
}
//将坐标放入数组
if(isPlace){
rays[i]=x;
rays[i+1]=y;
}
isPlace = true;
}
for (int i = 0; i < GameUtil.RAY_MAX*2; i=i+2) {
GameUtil.DATA_BOTTOM[rays[i]][rays[i+1]]=-1;
}
}
}
5.GameWin类
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class GameWin extends JFrame {
int wigth = 2 * GameUtil.OFFSET + GameUtil.MAP_W * GameUtil.SQUARE_LENGTH;
int height = 4 * GameUtil.OFFSET + GameUtil.MAP_H * GameUtil.SQUARE_LENGTH;
Image offScreenImage = null;
MapBottom mapBottom = new MapBottom();
MapTop mapTop = new MapTop();
GameSelect gameSelect = new GameSelect();
//是否开始,f未开始,t开始
boolean begin=false;
void launch(){
GameUtil.START_TIME=System.currentTimeMillis();
this.setVisible(true);
if(GameUtil.state==3){
this.setSize(500,500);
}else {
this.setSize(wigth,height);
}
this.setLocationRelativeTo(null);
this.setTitle("扫雷游戏");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
//鼠标事件
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
switch (GameUtil.state){
case 0 :
if(e.getButton()==1){
GameUtil.MOUSE_X = e.getX();
GameUtil.MOUSE_Y = e.getY();
GameUtil.LEFT = true;
}
if(e.getButton()==3) {
GameUtil.MOUSE_X = e.getX();
GameUtil.MOUSE_Y = e.getY();
GameUtil.RIGHT = true;
}
case 1 :
case 2 :
if(e.getButton()==1){
if(e.getX()>GameUtil.OFFSET + GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W/2)
&& e.getX()GameUtil.OFFSET
&& e.getY()100&&GameUtil.MOUSE_X<400){
if(GameUtil.MOUSE_Y>50&&GameUtil.MOUSE_Y<150){
GameUtil.level=1;
GameUtil.state=0;
return true;
}
if(GameUtil.MOUSE_Y>200&&GameUtil.MOUSE_Y<300){
GameUtil.level=2;
GameUtil.state=0;
return true;
}
if(GameUtil.MOUSE_Y>350&&GameUtil.MOUSE_Y<450){
GameUtil.level=3;
GameUtil.state=0;
return true;
}
}
return false;
}
void paintSelf(Graphics g){
g.setColor(Color.black);
g.drawRect(100,50,300,100);
GameUtil.drawWord(g,"简单",220,100,30,Color.black);
g.drawRect(100,200,300,100);
GameUtil.drawWord(g,"普通",220,250,30,Color.black)
g.drawRect(100,350,300,100);
GameUtil.drawWord(g,"困难",220,400,30,Color.black);
}
void hard(int level){
switch (level){
case 1:
GameUtil.RAY_MAX = 10;
GameUtil.MAP_W = 9;
GameUtil.MAP_H = 9;
break;
case 2:
GameUtil.RAY_MAX = 40;
GameUtil.MAP_W = 16;
GameUtil.MAP_H = 16;
break;
case 3:
GameUtil.RAY_MAX = 99;
GameUtil.MAP_W = 30;
GameUtil.MAP_H = 16;
break;
default:
}
}
}
源文件: 大学生Java课程设计——挖地雷游戏设计报告源文件-其它文档类资源-CSDN文库