用java实现扫雷小游戏,不再枯燥学语法(五)

写在开始

上一篇博客的链接为:用java实现扫雷小游戏,不再枯燥学语法(四)

任务清单

博主做这个项目给自己列的任务清单如下:

任务一:创建窗口(已完成)

任务二:窗口布局(已完成)

任务三:定义数据结构(已完成)

任务四:实现游戏的基础功能(布雷,查雷,排雷,判定输赢)(已完成)

任务五:辅助功能(待开数,已开数,游戏重开,计时器)

代码实现

这一部分的代码需要对之前三部分的代码进行添加和重构,因此博主把完整的三部分代码给出。

Game.java

package game;

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

public class Game implements ActionListener {
//  定义计时器
    public Timer timer=new Timer(1000,this);
//  实例化对象
    Function function = new Function();
    Windows windows = new Windows();

//  定义地图大小及数据结构
    public static int ROW = 10;
    public static int COL = 10;
    public static int[][] data = new int[ROW][COL];
    public static JButton[][] buttons = new JButton[ROW][COL];
//  定义地雷的个数
    public static int LEICOUNT = 10;
//  定义表示地雷的特征数
    public static int LEICODE = -1;
//  定义状态栏参数
    public static int UNOPEN = ROW * COL;
    public static int OPENED = 0;
    public static int second = 0;

//  创建游戏过程
    public Game() {
//      调用创建窗口
        windows.createWindows();
//      创建顶部状态框
        windows.setHeader();
//      添加地雷
        function.add();
//      设置按钮
        windows.setButtons();
//      启动计时器
        timer.start();
    }

//  主方法函数,启动游戏
    public static void main(String[] args) {
        new Game();
    }

//  编写监听器
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() instanceof Timer) {
            second++;
            Windows.label3.setText("用时" + second + "s");
            timer.start();
            return;
        }
    }
}

Function.java

package game;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import static game.Game.*;

public class Function implements ActionListener{
//  定义计时器
    public Timer timer=new Timer(1000,this);
//  添加地雷
    public void add(){
        Random random=new Random();
        for(int i=0;i<LEICOUNT;){
//          使用随机数生成地雷
            int r=random.nextInt(ROW);
            int c=random.nextInt(COL);
//          避免生成位置相同
            if(data[r][c]!=LEICODE){
                data[r][c]=LEICODE;
                i++;
            }
        }
//      计算周围雷的数量
        for (int i=0;i< ROW;i++){
            for(int j=0;j<COL;j++){
                if(data[i][j]==LEICODE) continue;
                int tempcount=0;
                if(i>0 && j>0 && data[i-1][j-1]==LEICODE)
                    tempcount++;
                if(i>0 && data[i-1][j]==LEICODE)
                    tempcount++;
                if(i>0 && j<COL-1 && data[i-1][j+1]==LEICODE)
                    tempcount++;
                if(j>0 && data[i][j-1]==LEICODE)
                    tempcount++;
                if(j<COL-1 && data[i][j+1]==LEICODE)
                    tempcount++;
                if(i<ROW-1 && j>0 && data[i+1][j-1]==LEICODE)
                    tempcount++;
                if(i<ROW-1 && data[i+1][j]==LEICODE)
                    tempcount++;
                if(i<ROW-1 && j<COL-1 && data[i+1][j+1]==LEICODE)
                    tempcount++;
//              格子中记录周围炸弹数
                data[i][j]=tempcount;
            }
        }
    }

//  检验是否胜利
    public void checkWin(){
        int count=0;
        for(int i=0;i<ROW;i++){
            for(int j=0;j<COL;j++){
                if(buttons[i][j].isEnabled())count++;
            }
        }
        if(count==LEICOUNT){
//          停止计时器,并输出提示信息
            timer.stop();
            JOptionPane.showMessageDialog(Windows.frame,"你成功了!");
        }
    }

//  打开格子
    public void openCell(int i,int j){
        JButton btn=buttons[i][j];
//      去除已经打开的格子
        if(!btn.isEnabled())
            return;
//      设置打开格子的效果
        btn.setEnabled(false);
        btn.setOpaque(true);
        btn.setBackground(Color.green);
        btn.setText(data[i][j]+"");
//      调用方法,更新label中的值
        updateCount();
//      使用递归的思想,实现级联打开
        if(data[i][j]==0){
            if(i>0 && j>0 && data[i-1][j-1]==0)
                openCell(i-1,j-1);
            if(i>0 && data[i-1][j]==0)
                openCell(i-1,j);
            if(i>0 && j<COL-1 && data[i-1][j+1]==0)
                openCell(i-1,j+1);
            if(j>0 && data[i][j-1]==0)
                openCell(i,j-1);
            if(j<COL-1 && data[i][j+1]==0)
                openCell(i,j+1);
            if(i<ROW-1 && j>0 && data[i+1][j-1]==0)
                openCell(i+1,j-1);
            if(i<ROW-1 && data[i+1][j]==0)
                openCell(i+1,j);
            if(i<ROW-1 && j<COL-1 &&data[i+1][j+1]==0)
                openCell(i+1,j+1);
        }
    }

//  更新label中的值
    public void updateCount() {
        OPENED++;
        UNOPEN--;
        Windows.label1.setText("待开:"+UNOPEN);
        Windows.label2.setText("已开:"+OPENED);
    }

//  检验是否踩雷
    public void Lose(){
        for(int i=0;i<ROW;i++){
            for(int j=0;j<COL;j++){
                JButton btn= buttons[i][j];
                if(data[i][j]==LEICODE){
                    btn.setBackground(Color.red);
                    btn.setEnabled(false);
                }else{
                    btn.setEnabled(false);
                    btn.setOpaque(true);
                    btn.setText(data[i][j]+"");
                }
            }
        }
//      停止计时器,并输出提示信息
        timer.stop();
        JOptionPane.showMessageDialog(Windows.frame,"你踩雷了!");
    }

//  重新开始
    public void restart(){
//      重置格子
        for(int i=0;i<ROW;i++){
            for(int j=0;j<COL;j++){
                data[i][j]=0;
                buttons[i][j].setBackground(Color.yellow);
                buttons[i][j].setEnabled(true);
                buttons[i][j].setText("");
            }
        }
//      重置参数
        UNOPEN=ROW*COL;
        OPENED=0;
        second=0;
//      重置状态栏
        Windows.label1.setText("待开:"+UNOPEN);
        Windows.label2.setText("未开:"+OPENED);
        Windows.label3.setText("用时:"+second+"s");
//      重新添加地雷
        add();
//      重新启动计时器
        timer.start();
    }

//  编写监听器,用于计时
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() instanceof Timer) {
            second++;
            Windows.label3.setText("用时" + second + "s");
            timer.start();
            return;
        }
    }
}

Windows.java

package game;

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

import static game.Game.*;

public class Windows implements ActionListener {

//  创建公共静态对象方便调用
    public static JFrame frame=new JFrame();
    public static JButton jButton=new JButton();
    public static JLabel label1=new JLabel("待开:"+UNOPEN);
    public static JLabel label2=new JLabel("已开:"+OPENED);
    public static JLabel label3=new JLabel("用时:"+second+"s");

//  定义计时器
    public Timer timer=new Timer(1000,this);
//  创建实例对象
    Function function=new Function();

//  创建窗口
    public void createWindows(){
//      定义窗口大小(600*700)
        frame.setSize(600,700);
//      窗口是否可以改变大小
        frame.setResizable(false);
//      点击可关闭
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//      设置窗口布局
        frame.setLayout(new BorderLayout());
//      显示窗口
        frame.setVisible(true);
    }

    //  创建的按钮放窗口顶部
    public void setHeader(){
//      创建布局
        JPanel panel=new JPanel(new GridBagLayout());
        GridBagConstraints c1=new GridBagConstraints(0,0,3,1,1.0,1.0,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0);
        panel.add(jButton,c1);
        jButton.addActionListener(this);

//      设置透明度
        label1.setOpaque(true);
//      设置背景色
        label1.setBackground(Color.white);
//      设置边框颜色
        label1.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
//      label2、label3同理
        label2.setOpaque(true);
        label2.setBackground(Color.white);
        label2.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
        label3.setOpaque(true);
        label3.setBackground(Color.white);
        label3.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
//      设置三个状态显示栏
        GridBagConstraints c2=new GridBagConstraints(0,1,1,1,1.0,1.0,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0);
        panel.add(label1,c2);
        GridBagConstraints c3=new GridBagConstraints(1,1,1,1,1.0,1.0,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0);
        panel.add(label2,c3);
        GridBagConstraints c4=new GridBagConstraints(2,1,1,1,1.0,1.0,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0);
        panel.add(label3,c4);
//      添加至布局的上方
        frame.add(panel,BorderLayout.NORTH);
    }

//  设置按钮
    public void setButtons(){
//      创建容器
        Container container=new Container();
//      创建布局
        container.setLayout(new GridLayout(ROW,COL));
//      添加至整体布局的中央
        frame.add(container,BorderLayout.CENTER);
//      创建按钮
        for(int i=0;i<ROW;i++){
            for(int j=0;j<COL;j++){
                JButton btn=new JButton();
                btn.setOpaque(true);
                btn.setBackground(Color.yellow);
                btn.addActionListener(this);
                container.add(btn);
                buttons[i][j]=btn;
            }
        }
    }

//  编写监听器工作
    public void actionPerformed(ActionEvent e) {
//      用于计时
        if(e.getSource() instanceof Timer){
            second++;
            label3.setText("用时"+second+"s");
            timer.start();
            return;
        }
//      按键响应
        JButton btn=(JButton) e.getSource();
//      重开按键响应
        if(btn.equals(jButton))
            function.restart();
//      游戏功能判定
        for(int i=0;i<ROW;i++){
            for(int j=0;j<COL;j++){
                if(btn.equals(buttons[i][j])){
                    if(data[i][j]==LEICODE){
                        function.Lose();
                    }else {
                        function.openCell(i, j);
                        function.checkWin();
                    }
                    return;
                }
            }
        }
    }
}

总结

这部分的代码改动主要是增加了监听器。

三个类都继承了ActionListener接口,同时在该类下面重写actionPerformed()方法,在该方法下定义监听器的执行内容。

结语

如果说这篇文章有让你学到一定的知识的话,不妨点个赞和关注,让博主能够看到。如果讲解中有什么错误和疏忽,也劳烦在评论中指出或提问,博主会第一时间进行更新和答复,谢谢!

发布了9 篇原创文章 · 获赞 11 · 访问量 2460

猜你喜欢

转载自blog.csdn.net/weixin_44018540/article/details/104048054