飞道的博客

java扫雷游戏,触雷时学姐哭着扑向了你!

219人阅读  评论(0)

引言:

当年我还是学生时候,同学基本都是人手一台台式机,但我们那时都还不太会电脑,又恰逢病毒盛行的年代,不太会打理电脑,所以经常电脑中毒什么的、毛病一堆,出了问题也不会搞,我有个室友高中的时候学了重装系统(他自己说他跟师傅学过修电脑,逮谁都这么说),黑色小皮包里装了个光盘(他人又长得跟伙夫样的胖胖憨憨的,像极了夹着皮包的大款),每当他把舍友或者同学的电脑重装好以后,周围男同学的眼神都充满了慈祥!更何况女同学,那是一口一个“好厉害哦,小***,好棒哦,*** ”,我都要吐了,于是经常有女同学们请他去女生宿舍修电脑(女宿舍一般是不让进去的)、修完电脑请他吃饭,回来就跟我们吹牛逼,我不听我不听,我呸。。。,这样就拥有了优先择偶权?慢慢的我们发现他并不像表面看起来那么憨憨的(大家懂的)。后面我们还发现他其实就只会重装系统,也就会那么两句命令,然而。。。,可恶,给他装到了!!

我们那时候上机经常玩扫雷,试想如果我当年可以用 java 写个扫雷出来,那场面不用我多说了吧,大家让开,我要开始装逼了(你们帮我想,留言给我,我看看你们都能想到啥),只能说我太纯洁了,我肯定做满雷,让师姐师妹点开,不说了我咽口水先。至于现在的话我就不确定咯,毕竟我已经老了。

之前用JavaScript写过了一个扫雷,这次我用java再写了一遍,权当是复习咯。用两种语言写,虽然有相似之处,但区别也蛮大,基本等于重写。

效果图

      

实现思路

1.界面上可以点开的各种实际都是按钮,创建9行9列的二维数组,然后根据这个数组来创建JButton。

2.对应创建二维数组data,用来存取数据,0表示周围无雷,-1表示当前是雷,其他数字表示周围雷的数量。

3.对应创建二维数组state,用来存取按钮状态,0未打开,1 打开  2旗子  3 未知(控制显示对应的图标)

4.设置雷:随机行数 i 和列数 j,根据随机到 i、j 从二维数组data中取出对应的元素值,若值不为-1(不是雷),则将此元素data[i][j]设置为-1,若值是-1(已经是雷了),则跳过,不管是否跳过都进行递归,直到雷的数量达到设定的最大数量,跳出递归。

5.设置周围雷的数量:计算每个元素周围的雷数量(周围指的是 左上、上、右上、右、右下、下、左下、左 这8个位置),循环二维数组data,判断当前值不是-1,则需要计算周围雷的数量,等会细说。

6.有任一格子被揭开,则游戏开始并且计时,当格子被揭开的时候分3种情况

(1)格子是雷,执行爆炸动画,游戏结束。

(2)当前格子周围有雷,则仅仅打开此格子,对应显示周围雷数量的数字图片。

(3)当前格子不是雷且周围没有雷(data取到的元素值为0),则依次打开周围,并且被打开的周围元素也没有雷的情况下,继续打开(递归)。

7.右键可以进行插小旗、打问号等操作(对数组state进行的操作)。

代码实现

设置头部


  
  1. //设置头部
  2. private void setHeader() {
  3. Container container = new Container();
  4. container.setLayout( new GridLayout( 1, 3));
  5. timeJLabel = new JLabel( "时间:"+time,JLabel.CENTER);
  6. timeJLabel.setForeground(Color.DARK_GRAY);
  7. timeJLabel.setFont( new Font( "微软雅黑",Font.BOLD, 16));
  8. leiJLabel = new JLabel( "雷:"+curLeiCount,JLabel.CENTER);
  9. leiJLabel.setForeground(Color.DARK_GRAY);
  10. leiJLabel.setFont( new Font( "微软雅黑",Font.BOLD, 16));
  11. reStart = new JButton((ImageIcon)imageMap. get( 21));
  12. Dimension preferredSize = new Dimension( 100, 40);
  13. reStart.setPreferredSize(preferredSize);
  14. reStart.addActionListener( this);
  15. //注意添加顺序
  16. container. add(timeJLabel);
  17. container. add(reStart);
  18. container. add(leiJLabel);
  19. mainFrame. add(container,BorderLayout.NORTH);
  20. }

设置游戏区域按钮

1.创建容器,并采用GridLayout 布局。

2.根据设定的ROWS、COLS创建二维数组,数组存储JButton,给每个按钮设置图标。

3.给每个按钮添加鼠标点击事件,右键事件。


  
  1. private void setButtons() {
  2. Container container = new Container();
  3. container.setLayout( new GridLayout(ROWS, COLS));
  4. ImageIcon icon= null;
  5. for ( int i = 0; i <ROWS; i++) {
  6. for ( int j = 0; j < COLS; j++) {
  7. JButton btn = new JButton();
  8. btn.setBounds( 0, 0, 39, 39);
  9. icon = (ImageIcon)imageMap. get( 10);
  10. setBtnImage(btn,icon);
  11. container. add(btn);
  12. btns[i][j]=btn;
  13. btn.addActionListener( this);
  14. btn.addMouseListener( this);
  15. }
  16. }
  17. mainFrame. add(container,BorderLayout.CENTER);
  18. }

设置雷

1.随机行数 i 和列数 j,根据随机到 i、j 从二维数组data中取出对应的元素值。

2.判断值,若值不为-1(不是雷),则将此元素data[i][j]设置为-1,若值是-1(已经是雷了),则跳过。

3.不管上一步是否跳过都进行递归,直到雷数量达到设定的最大数量,跳出递归。


  
  1. private void setLei() {
  2. if(computedLeiCount==LEICOUNT){ //如果达到雷的最大数量则跳出
  3. return;
  4. }
  5. Random random = new Random();
  6. int r = random.nextInt(ROWS);
  7. int c = random.nextInt(COLS);
  8. //0 无; -1表示雷 ; 其他表示周围的雷数量
  9. if(data[r][c]!= -1){ //如果不是雷则设置为雷
  10. data[r][c]= -1;
  11. computedLeiCount++;
  12. }
  13. setLei(); //递归调用
  14. }

计算周围雷的数量并显示

1.循环之前的二维数组data,元素值是-1(雷)跳过,不是-1则继续。

2.如果当前元素的下标是(i,j),则左上为(i-1,j-1),上为(i-1,j ),右上为(i-1,j+1),以此类推,如下图所示:

3.分别取出这8个元素,并判断他们是不是雷,如果是则计数累加,最后把这个计数赋值给元素data[i][j]。


  
  1. //设置周围雷的数量
  2. private void setAroundLei() {
  3. for (int i = 0; i < ROWS; i++) {
  4. for (int j = 0; j < COLS; j++) {
  5. if( data[i][j]!=-1){如果当前不是雷,则判断他周围有几个雷,并设置值
  6. data[i][j] = computedLei(i,j);
  7. }
  8. }
  9. }
  10. }
  11. //计算周围雷的数量
  12. private int computedLei(int i,int j) {
  13. int count= 0;
  14. //左上
  15. int ci = i -1;
  16. int cj = j -1;
  17. if(ci>= 0 && cj>= 0){
  18. if( data[ci][cj]==-1){
  19. count++;
  20. }
  21. }
  22. //上
  23. ci = i -1;
  24. cj = j;
  25. if(ci>= 0){
  26. if( data[ci][cj]==-1){
  27. count++;
  28. }
  29. }
  30. //右上
  31. ci = i -1;
  32. cj = j+ 1;
  33. if(ci>= 0 && cj< COLS){
  34. if( data[ci][cj]==-1){
  35. count++;
  36. }
  37. }
  38. //右
  39. ci = i;
  40. cj = j+ 1;
  41. if(cj< COLS){
  42. if( data[ci][cj]==-1){
  43. count++;
  44. }
  45. }
  46. //右下
  47. ci = i+ 1;
  48. cj = j+ 1;
  49. if(ci< ROWS && cj< COLS){
  50. if( data[ci][cj]==-1){
  51. count++;
  52. }
  53. }
  54. //下
  55. ci = i+ 1;
  56. cj = j;
  57. if(ci< ROWS){
  58. if( data[ci][cj]==-1){
  59. count++;
  60. }
  61. }
  62. //左下
  63. ci = i+ 1;
  64. cj = j -1;
  65. if(ci< ROWS && cj >= 0){
  66. if( data[ci][cj]==-1){
  67. count++;
  68. }
  69. }
  70. //左
  71. ci = i;
  72. cj = j -1;
  73. if(cj >= 0){
  74. if( data[ci][cj]==-1){
  75. count++;
  76. }
  77. }
  78. return count;
  79. }

添加点击事件

1.让代码实现 ActionListener

2.重写方法actionPerformed,获取点击的按钮进行揭开操作(分3种情况):

(1)格子是雷,执行爆炸动画,游戏结束。

(2)当前格子周围有雷,则仅仅打开此格子,显示周围雷数量的数字图片。

(3)当前格子不是雷且周围没有雷(data取到的元素值为0),则依次打开周围,并且被打开的周围元素也没有雷的情况下,继续打开(递归)。

打开指定按钮


  
  1. //打开指定的button
  2. private void open(int i,int j) {
  3. JButton button = btns[i][j];
  4. if(state[i][j]== 1){ //已经打开直接返回
  5. return ;
  6. }
  7. state[i][j]= 1; //设置打开状态
  8. int num = data[i][j];
  9. if(num== -1){ //直接使用雷的图片
  10. setBtnImage(button,(ImageIcon)imageMap. get( 18));
  11. //游戏结束,并爆炸
  12. boom(button);
  13. } else{ //如果当前不是雷,显示对应数字类图片
  14. if(num== 0){
  15. num= 9;
  16. //显示周围的图标,并且递归
  17. openAround(i,j);
  18. }
  19. setBtnImage(button,(ImageIcon)imageMap. get(num));
  20. setBtnEnabled(button, false); //按钮被打开设置不能再点击了
  21. //判定是否成功
  22. successOrNot( 1);
  23. }
  24. }

触雷爆炸

爆炸采用线程来执行,就是切换图片,图片切换到最后一张后线程结束,回调定义好的方法进行结束提示、打开所有格子等操作。


  
  1. //爆炸效果
  2. private void boom(JButton button) {
  3. flag= true;
  4. GameRunnable gameRunnable = new GameRunnable();
  5. gameRunnable.setParam(button, boomImageMap, this);
  6. thread = new Thread(gameRunnable);
  7. thread.start();
  8. }
  9. //爆炸回调方法
  10. public void reback(JButton button) {
  11. setBtnImage(button,(ImageIcon)imageMap. get( 18));
  12. flag= false;
  13. //设置全部按钮打开
  14. openAll();
  15. //弹出结束提示
  16. UIManager.put( "OptionPane.buttonFont", new FontUIResource( new Font( "宋体", Font.ITALIC, 13)));
  17. UIManager.put( "OptionPane.messageFont", new FontUIResource( new Font( "宋体", Font.ITALIC, 13)));
  18. JOptionPane.showMessageDialog(mainFrame, "你失败了!点击上方按钮重新开始");
  19. }

递归打开周围


  
  1. //打开周围
  2. private void openAround(int i,int j) {
  3. //左上
  4. int ci = i- 1;
  5. int cj = j- 1;
  6. if(ci>= 0 && cj>= 0){
  7. open(ci,cj);
  8. }
  9. //上
  10. ci = i- 1;
  11. cj = j;
  12. if(ci>= 0){
  13. open(ci,cj);
  14. }
  15. //右上
  16. ci = i- 1;
  17. cj = j+ 1;
  18. if(ci>= 0 && cj<COLS){
  19. open(ci,cj);
  20. }
  21. //右
  22. ci = i;
  23. cj = j+ 1;
  24. if(cj<COLS){
  25. open(ci,cj);
  26. }
  27. //右下
  28. ci = i+ 1;
  29. cj = j+ 1;
  30. if(ci<ROWS && cj<COLS){
  31. open(ci,cj);
  32. }
  33. //下
  34. ci = i+ 1;
  35. cj = j;
  36. if(ci<ROWS){
  37. open(ci,cj);
  38. }
  39. //左下
  40. ci = i+ 1;
  41. cj = j- 1;
  42. if(ci<ROWS && cj >= 0){
  43. open(ci,cj);
  44. }
  45. //左
  46. ci = i;
  47. cj = j- 1;
  48. if(cj >= 0){
  49. open(ci,cj);
  50. }
  51. }

鼠标右键事件

1.实现MouseListener,重写mouseClicked方法。

2.如果按钮是未打开状态则设置为旗子(设置state数组对应的元素值:2)。

3.如果按钮是旗子状态则设置为未知(设置state数组对应的元素值:3)。

4.如果按钮是未知状态则设置为原来的状态未打开(设置state数组对应的元素值:0)。


  
  1. //鼠标右键的处理
  2. @Override
  3. public void mouseClicked(MouseEvent e) {
  4. if(e.getButton()==MouseEvent.BUTTON3){
  5. JButton button = (JButton)e.getSource();
  6. for ( int i = 0; i <ROWS; i++) {
  7. for ( int j = 0; j < COLS; j++) {
  8. if(button. equals(btns[i][j])){ //找到对应的按钮
  9. if(state[i][j]== 0){ //如果是未打开状态则设置为旗子
  10. state[i][j]= 2;
  11. setBtnImage(button,(ImageIcon)imageMap. get( 12));
  12. curLeiCount--;
  13. leiJLabel.setText( "雷:"+curLeiCount);
  14. //需求判断是否成功
  15. successOrNot( 2);
  16. } else if(state[i][j]== 2){ //如果是旗子状态则设置为未知
  17. state[i][j]= 3;
  18. setBtnImage(button,(ImageIcon)imageMap. get( 13));
  19. curLeiCount++;
  20. leiJLabel.setText( "雷:"+curLeiCount);
  21. } else if(state[i][j]== 3){ //如果是未知状态则设置为原来的未打开
  22. state[i][j]= 0;
  23. setBtnImage(button,(ImageIcon)imageMap. get( 10));
  24. }
  25. }
  26. }
  27. }
  28. }
  29. }

胜利判定

1.判断旗子的位置是不是正确的雷,并统计数量,如果统计到的数量刚好和设定的雷总数一样,证明雷全部被查出了,判定为胜利。

2.如果未打开的数量与设定雷的总数一样,也判定为胜利。


  
  1. //判断是否成功 参数type=2表示判断右键插旗子,参数 type=其他 表示判断鼠标点击
  2. private void successOrNot(int type) {
  3. int count= 0;
  4. if(type== 2){
  5. /*
  6. * 1.判断旗子的位置是否是正确的雷,并统计数量
  7. * 2.如果统计到的数量刚好和雷的总数一样,证明全部被查出了,判定为胜利
  8. */
  9. for ( int i = 0; i <ROWS; i++) {
  10. for ( int j = 0; j < COLS; j++) {
  11. if(state[i][j]== 2 && data[i][j]== -1){ //是旗子,也是雷,则计数
  12. count++;
  13. }
  14. }
  15. }
  16. } else{
  17. //最终的未打开的数量与雷的数量一样,则表示成功
  18. for ( int i = 0; i <ROWS; i++) {
  19. for ( int j = 0; j < COLS; j++) {
  20. if(state[i][j]!= 1){ //未打开就计数
  21. count++;
  22. }
  23. }
  24. }
  25. }
  26. if(count==LEICOUNT){ //成功
  27. //关闭计时线程
  28. gameTimeRunnable.setFlag( false);
  29. //设置全部按钮打开
  30. openAll();
  31. //弹出结束提示
  32. UIManager.put( "OptionPane.buttonFont", new FontUIResource( new Font( "宋体", Font.ITALIC, 13)));
  33. UIManager.put( "OptionPane.messageFont", new FontUIResource( new Font( "宋体", Font.ITALIC, 13)));
  34. JOptionPane.showMessageDialog(mainFrame, "恭喜你获得了胜利!你太棒了");
  35. }
  36. }

最后加入重新开始事件就完成了。

重新开始就是重新设置相关参数。


  
  1. //重新开始游戏
  2. private void restart() {
  3. //关闭计时线程(可能正在进行游戏,所以要把计时线程关闭)
  4. gameTimeRunnable.setFlag( false);
  5. startFlag= false;
  6. computedLeiCount= 0;
  7. curLeiCount= 10;
  8. leiJLabel.setText( "雷:"+curLeiCount);
  9. time= 0;
  10. timeJLabel.setText( "时间:"+time);
  11. data= new int[ROWS][COLS]; //存取数据
  12. state= new int[ROWS][COLS]; //存取打开状态,0未打开,1 打开
  13. setLei();
  14. setAroundLei();
  15. ImageIcon icon = null;
  16. for ( int i = 0; i <ROWS; i++) {
  17. for ( int j = 0; j < COLS; j++) {
  18. icon = (ImageIcon)imageMap. get( 10);
  19. setBtnImage(btns[i][j],icon);
  20. setBtnEnabled(btns[i][j], true); //按钮重新设置可以点击
  21. }
  22. }
  23. }

看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏 ,能【关注】一波就更好了。

需要源码的留言给我或者私信我,我发给你

 

 更多源码

♥ java坦克大战,回忆童年!

♥ 基于javascript扫雷小游戏,以前上学经常玩

♥ java学生宿舍管理系统

♥ 学生成绩管理系统

♥ 基于JavaScript植物大战僵尸(附源码)

♥ 老父亲给女儿做的下雪特效,满足女儿看雪的愿望(附源码)

♥ 基于canvas的九宫格抽奖特效(附源码)

♥ 抖音很火的华为太空人表盘(附源码)

♥ 基于JavaScript页面动态验证码(附源码)

♥ JavaScript俄罗斯方块小游戏(附源码)

♥ 基于JavaScript的贪吃蛇游戏(附源码)


转载:https://blog.csdn.net/dkm123456/article/details/116637184
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场