写在开始
上一篇博客的链接为:用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()方法,在该方法下定义监听器的执行内容。
结语
如果说这篇文章有让你学到一定的知识的话,不妨点个赞和关注,让博主能够看到。如果讲解中有什么错误和疏忽,也劳烦在评论中指出或提问,博主会第一时间进行更新和答复,谢谢!