基于java的挖地雷游戏
2015-08-18 12:53
549 查看
项目简介:
模拟windows系统中的挖地雷游戏,采用Java开发类似的游戏。
游戏包含的行为:
1. 基本的游戏功能1(优先级高):
通过鼠标点击按钮,完成图片的显示:
Case1: 如果是空白,则显示空白以及与之相连的空白;
Case2: 如果是数字,则只显示数字本身;
Case3: 如果是地雷,游戏结束;
2. 时钟功能(优先级低):
时钟记录,在游戏过程中显示玩家到目前为止所花费的时间;
3. 关卡功能(优先级低):
玩家可以在容易,中,难三个等级中选择游戏的难度;
根据游戏行为的简单设计:
1. 参考拼图游戏,鼠标点击完成翻图,只是对于button的一个setIcon的动作;
2. 针对地雷阵列,为描述其中每一个的状态,需要建立一对应的二维数组,二维数组中地数字表示该位置的状态:
-1:表示本格为地雷;
0:表示本格空白;
1-8:表示本格周围有多少个地雷;
说明:那么游戏的关卡难度,就是地雷阵列的大小以及地雷的多少,不同的难度只要在创建地雷二维数据时,对地雷阵列大小和地雷多少进行制定即可。
创建2维数组时的顺序:
在二维数组中首先随机地确立需要数量的地雷的位置;后续地可以简单的有两种方案,
方案1:以地雷的位置为核心,先设定地雷周围的方格的状态,再对剩下的方格进行设定;
每个地雷周边最多8个空格;只要将这8个处理完即可。
那么对于每个空格而言,其对应的处理过程:
aroundCount(int x, inty){
int count =0;
if(valid(x,y)){
for(int i=0; i<7; i++)
{
If(valid(x+node[i].x,y+node[i].y){
Count++;
}
}
}
}
VoidmineAround(int x, int y)
{
for(inti=0; i<7; i++)
{
Inttempx = x+node[i].x;
Inttempy = y+node[i].y;
If(valid(tempx,tempy){
Touch(x, y,count);
}
}
}
方案2:按照自上而下,自左而右的顺序,依次扫描二维数组中的每个除地雷外的格子,并且完成数值的设定;
方案2的逻辑更加简单,只要数数当前方格的周围有多个个地雷即可。而方案1要先找到当前某地雷周围的方格,然后再根据方格周围有多少个地雷来设定值;但从数据的处理量而言,方案1面对的数据量更小点,所以我们还是从效率出发,选择方案1。
扫描法中的一个重点在于:要找到当前方格的所有邻居;边界点的存在增加找邻居的困难。
3. 鼠标的点击动作:
每个鼠标监听类在创建时,会制定其对应的x值和y值,此点击时,可以获得本按钮的x值和y值;从而获得二维数组中对应的值,根据对应的值进行操作:
3.1 该值是数字1-8:直接显示;
3.2 该值是-1:游戏失败;
3.3 该值是0:显示自身的同时,找到其周围的邻居中值为0的按钮,进行显示;
此处使用的基本知识为遍历:
walkThrough();
此处可以采用深度优先的方式找到所有相连的值为0或数字的方格:
walkthrough(intx, int y){
if(value(x,y)!=0)
return;
if( isValid(x,y) && notTouched(x,y)){
touch(x,y);
if(value(x,y)==0{ //为零的情况下,向8个方向扩展;
walkthrough(x-1, y-1); //往其他的8个方向探索;
walkthrough(x, y-1);
…
Walkthrough(x+1, y+1);
}
}
}
4. 判断游戏的结束:
失败的情况1:只要点击到地雷,游戏即结束;
成功的情况2:每完成一次点击后,判断当前剩下的未被touch的空格数;如果未被touch的空格数刚好等同于地雷的个数,那就说明游戏成功结束;
挖地雷游戏的实现
首先,回顾下主要的设计:
根据设计中的情况:
单元格设计:
单元格对象box.java,比如当前单元格的x,y,以及是否被touch(即点击过)?是否设置过值?
方格阵列设计:
单元格组成的阵列也包含特定的属性:
1. 横向的x值和纵向的y值大小,即阵列范围;
2. 包含的地雷的个数;
3. 剩余的未被touch的方格数;
4. 阵列所对应的方格二维数组;
实现1:
将单元方格作为一基本的类,box.java;包含方格的坐标已经对应的状态;并且存储对应的值;
实现2:
将所有的单元方格组成的二维结构作为一基本的类,block.java;
提供touch(x,y)方法;
提供walkThrough方法;
提供valid方法;
实现3:
鼠标的响应类:其中主要的行为是根据点击的行为,按三种方式进行处理;
根据点击鼠标的x和y值,获得对应方格阵列中的目标方格,在根据目标方格的值状态:
1. 如果-1:游戏结束;
2. 如果1-8:显示当前的方块;
3. 如果0:不仅显示当前的方块,还使用深度优先的策略,遍历附近其他的按钮;
如果上述三者的行为由box统一提供的话,那对于0的情况,就意味着box又要反过来依赖block;不喜欢这种相互依赖的设计;
如此的话,采用block类的层面来直接提供上述三种行为的支持。
实现4:
界面类,gameView.java,其中包含对各个类的初始化;
对按键的初始化;
对block的初始化等;
扫雷游戏实操过程中
问题与解决
问题1:按钮监听类中遇到的问题,
public class MyButton{
publicvoid actionPerformed(ActionEvent e){
boxclickedBox = blk.getBox(x, y);
if(!clickedBox.isTouched()){
inttempValue = clickedBox.getValue();
if(tempValue== MyConstant.MINE){
System.out.println("Youtouch mine, game over");
System.exit(0);
}elseif(tempValue == MyConstant.BLANK){
blk.walkThrough(x,y);
}elseif(tempValue >=1 && tempValue <=8){
…
}else{
}
}
}
上述是对按键处理:
获得被按键的value后,然后根据value的值来进行相应的动作;
如果踩到地雷,那相应的处理很简单;
如果踩到数字,那也好办;
关键的是踩到空白该如何处理?根据我们的设计,相应的采取遍历的方式;但是除了遍历的对象外,还要考虑对按钮进行设置,对应的box设置touched;感觉此处的几个对象纠缠在一起,耦合严重。
简单想到的方式:
对box的设置touched在blk的walkThrough中完成;而对按钮的设置,则在walkThrough返回结果后完成。
如上图,观察下windows下的扫雷游戏,点击到空格后,整个图会扩散直到边界和数字;所以可认为点击到数字是点击到空白的特殊情况。
walkThrough的返回值可采取容器类的方式来实现。
创建用于值传递的类point.java:
public class point{
public point(int x, int y){
This.x = x; this.y =y;
}
publicint getX(){return x;}
public int getY(){return y;}
}
但是,此处的walkThrough属于递归调用,所以其本身不适合返回值;所以在block类中,我们再创建一用于值传递的对象validpoint;
问题2 GameView的显示问题:
GameView.java中的代码:
public GameView(int x, int y,int nummine){
this.x = x;
this.y = y;
this.nummine =nummine;
this.setTitle("扫雷v0.1");
System.out.println("x*MyConstant.btnWidth="+x*MyConstant.btnWidth+""+"y*MyConstant.btnHeight="+y*MyConstant.btnHeight);
this.setSize(500,500);
this.setLayout(newGridLayout(10, 10));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addButton();
}
public void addButton(){
blk = new Block(x,y, nummine);
jbtn = newJButton[x][y];
for(int i=0; i<x;i++)
for(intj=0; j<y; j++)
{
jbtn[i][j]= new JButton();
jbtn[i][j].setBounds(x*MyConstant.btnWidth,y*MyConstant.btnHeight,MyConstant.btnWidth, MyConstant.btnHeight);
jbtn[i][j].addActionListener(newMyButton(x,y,blk,jbtn));
this.add(jbtn[i][j]);
}
}
如上的代码在运行中出现问题:预想的10*10的图片并不会马上出现,
之所以出现这种问题:
因为JFrame容器过早显示,会导致后面的控件没有准备好就显示出来,所以需要把JFrame的显示放在最后。
对应的解决方案:让JFrame的显示放到最后,即setVisible(true)这条语句放到button都安置完以后。
模拟windows系统中的挖地雷游戏,采用Java开发类似的游戏。
游戏包含的行为:
1. 基本的游戏功能1(优先级高):
通过鼠标点击按钮,完成图片的显示:
Case1: 如果是空白,则显示空白以及与之相连的空白;
Case2: 如果是数字,则只显示数字本身;
Case3: 如果是地雷,游戏结束;
2. 时钟功能(优先级低):
时钟记录,在游戏过程中显示玩家到目前为止所花费的时间;
3. 关卡功能(优先级低):
玩家可以在容易,中,难三个等级中选择游戏的难度;
根据游戏行为的简单设计:
1. 参考拼图游戏,鼠标点击完成翻图,只是对于button的一个setIcon的动作;
2. 针对地雷阵列,为描述其中每一个的状态,需要建立一对应的二维数组,二维数组中地数字表示该位置的状态:
-1:表示本格为地雷;
0:表示本格空白;
1-8:表示本格周围有多少个地雷;
说明:那么游戏的关卡难度,就是地雷阵列的大小以及地雷的多少,不同的难度只要在创建地雷二维数据时,对地雷阵列大小和地雷多少进行制定即可。
创建2维数组时的顺序:
在二维数组中首先随机地确立需要数量的地雷的位置;后续地可以简单的有两种方案,
方案1:以地雷的位置为核心,先设定地雷周围的方格的状态,再对剩下的方格进行设定;
每个地雷周边最多8个空格;只要将这8个处理完即可。
那么对于每个空格而言,其对应的处理过程:
aroundCount(int x, inty){
int count =0;
if(valid(x,y)){
for(int i=0; i<7; i++)
{
If(valid(x+node[i].x,y+node[i].y){
Count++;
}
}
}
}
VoidmineAround(int x, int y)
{
for(inti=0; i<7; i++)
{
Inttempx = x+node[i].x;
Inttempy = y+node[i].y;
If(valid(tempx,tempy){
Touch(x, y,count);
}
}
}
方案2:按照自上而下,自左而右的顺序,依次扫描二维数组中的每个除地雷外的格子,并且完成数值的设定;
方案2的逻辑更加简单,只要数数当前方格的周围有多个个地雷即可。而方案1要先找到当前某地雷周围的方格,然后再根据方格周围有多少个地雷来设定值;但从数据的处理量而言,方案1面对的数据量更小点,所以我们还是从效率出发,选择方案1。
扫描法中的一个重点在于:要找到当前方格的所有邻居;边界点的存在增加找邻居的困难。
3. 鼠标的点击动作:
每个鼠标监听类在创建时,会制定其对应的x值和y值,此点击时,可以获得本按钮的x值和y值;从而获得二维数组中对应的值,根据对应的值进行操作:
3.1 该值是数字1-8:直接显示;
3.2 该值是-1:游戏失败;
3.3 该值是0:显示自身的同时,找到其周围的邻居中值为0的按钮,进行显示;
此处使用的基本知识为遍历:
walkThrough();
此处可以采用深度优先的方式找到所有相连的值为0或数字的方格:
walkthrough(intx, int y){
if(value(x,y)!=0)
return;
if( isValid(x,y) && notTouched(x,y)){
touch(x,y);
if(value(x,y)==0{ //为零的情况下,向8个方向扩展;
walkthrough(x-1, y-1); //往其他的8个方向探索;
walkthrough(x, y-1);
…
Walkthrough(x+1, y+1);
}
}
}
4. 判断游戏的结束:
失败的情况1:只要点击到地雷,游戏即结束;
成功的情况2:每完成一次点击后,判断当前剩下的未被touch的空格数;如果未被touch的空格数刚好等同于地雷的个数,那就说明游戏成功结束;
挖地雷游戏的实现
首先,回顾下主要的设计:
根据设计中的情况:
单元格设计:
单元格对象box.java,比如当前单元格的x,y,以及是否被touch(即点击过)?是否设置过值?
方格阵列设计:
单元格组成的阵列也包含特定的属性:
1. 横向的x值和纵向的y值大小,即阵列范围;
2. 包含的地雷的个数;
3. 剩余的未被touch的方格数;
4. 阵列所对应的方格二维数组;
实现1:
将单元方格作为一基本的类,box.java;包含方格的坐标已经对应的状态;并且存储对应的值;
实现2:
将所有的单元方格组成的二维结构作为一基本的类,block.java;
提供touch(x,y)方法;
提供walkThrough方法;
提供valid方法;
实现3:
鼠标的响应类:其中主要的行为是根据点击的行为,按三种方式进行处理;
根据点击鼠标的x和y值,获得对应方格阵列中的目标方格,在根据目标方格的值状态:
1. 如果-1:游戏结束;
2. 如果1-8:显示当前的方块;
3. 如果0:不仅显示当前的方块,还使用深度优先的策略,遍历附近其他的按钮;
如果上述三者的行为由box统一提供的话,那对于0的情况,就意味着box又要反过来依赖block;不喜欢这种相互依赖的设计;
如此的话,采用block类的层面来直接提供上述三种行为的支持。
实现4:
界面类,gameView.java,其中包含对各个类的初始化;
对按键的初始化;
对block的初始化等;
扫雷游戏实操过程中
问题与解决
问题1:按钮监听类中遇到的问题,
public class MyButton{
publicvoid actionPerformed(ActionEvent e){
boxclickedBox = blk.getBox(x, y);
if(!clickedBox.isTouched()){
inttempValue = clickedBox.getValue();
if(tempValue== MyConstant.MINE){
System.out.println("Youtouch mine, game over");
System.exit(0);
}elseif(tempValue == MyConstant.BLANK){
blk.walkThrough(x,y);
}elseif(tempValue >=1 && tempValue <=8){
…
}else{
}
}
}
上述是对按键处理:
获得被按键的value后,然后根据value的值来进行相应的动作;
如果踩到地雷,那相应的处理很简单;
如果踩到数字,那也好办;
关键的是踩到空白该如何处理?根据我们的设计,相应的采取遍历的方式;但是除了遍历的对象外,还要考虑对按钮进行设置,对应的box设置touched;感觉此处的几个对象纠缠在一起,耦合严重。
简单想到的方式:
对box的设置touched在blk的walkThrough中完成;而对按钮的设置,则在walkThrough返回结果后完成。
如上图,观察下windows下的扫雷游戏,点击到空格后,整个图会扩散直到边界和数字;所以可认为点击到数字是点击到空白的特殊情况。
walkThrough的返回值可采取容器类的方式来实现。
创建用于值传递的类point.java:
public class point{
public point(int x, int y){
This.x = x; this.y =y;
}
publicint getX(){return x;}
public int getY(){return y;}
}
但是,此处的walkThrough属于递归调用,所以其本身不适合返回值;所以在block类中,我们再创建一用于值传递的对象validpoint;
问题2 GameView的显示问题:
GameView.java中的代码:
public GameView(int x, int y,int nummine){
this.x = x;
this.y = y;
this.nummine =nummine;
this.setTitle("扫雷v0.1");
System.out.println("x*MyConstant.btnWidth="+x*MyConstant.btnWidth+""+"y*MyConstant.btnHeight="+y*MyConstant.btnHeight);
this.setSize(500,500);
this.setLayout(newGridLayout(10, 10));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addButton();
}
public void addButton(){
blk = new Block(x,y, nummine);
jbtn = newJButton[x][y];
for(int i=0; i<x;i++)
for(intj=0; j<y; j++)
{
jbtn[i][j]= new JButton();
jbtn[i][j].setBounds(x*MyConstant.btnWidth,y*MyConstant.btnHeight,MyConstant.btnWidth, MyConstant.btnHeight);
jbtn[i][j].addActionListener(newMyButton(x,y,blk,jbtn));
this.add(jbtn[i][j]);
}
}
如上的代码在运行中出现问题:预想的10*10的图片并不会马上出现,
之所以出现这种问题:
因为JFrame容器过早显示,会导致后面的控件没有准备好就显示出来,所以需要把JFrame的显示放在最后。
对应的解决方案:让JFrame的显示放到最后,即setVisible(true)这条语句放到button都安置完以后。
相关文章推荐
- 泛型(java基础)
- thinking in java 学习笔记(一)
- Java深度历险(六)——Java注解 2015.8.18
- Java多线程实践之—终结
- 《精通hibernate:java持久化...》---持久化类
- 基于Java的拼图游戏
- springMvc里的mvc:resources与静态资源的访问
- Java的垃圾回收机制GC
- spring MVC项目中,欢迎页首页根路径到底是怎么设置的
- JAVA之Timer定时器
- Java总的Static关键字
- Java中创建对象的四种方法
- JAVA 用递归实现求五的阶乘
- JAVA创建对象的4种方式
- HBase的JAVA API操作详解
- java 之 面向对象
- java.lang.IllegalAccessError: class com.google.protobuf.HBaseZeroCopyByteString
- java.lang.IllegalAccessError: class com.google.protobuf.HBaseZeroCopyByteString
- 将activiti整合到spring里
- Java并发编程:Callable、Future和FutureTask