游戏流程
-
开始游戏弹出难度选择框
-
选择难度
-
根据难度生成游戏界面
-
翻开棋盘
- 是炸弹游戏结束显示所有地雷。
- 不是计算周围炸弹数
- 炸弹数不为0显示在该位置
- 炸弹数为0递归展开棋盘直到周围炸弹数不为0停止
-
翻开所有非炸弹后游戏结束
编写难度选择窗口
public class Main {
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main()::level);
}
private void level(){
frame = new JFrame("难度选择"); // 创建难度选择界面的窗口
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗口关闭操作
JPanel panel = new JPanel(); // 创建一个面板用于放置难度选择按钮
panel.setLayout(new GridLayout(1, 3)); // 使用GridLayout布局,将按钮排列成一行三列
JButton button1 = new JButton("初级"); // 创建初级难度按钮
button1.addActionListener(e->{
// 这里创建游戏界面
});
panel.add(button1); // 将初级难度按钮添加到面板
JButton button2 = new JButton("中级");
button2.addActionListener(e->{
});
panel.add(button2);
JButton button3 = new JButton("高级");
button3.addActionListener(e->{
});
panel.add(button3);
frame.getContentPane().add(panel);
frame.setSize(300, 100);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
效果展示
编写扫雷窗口
增加一个size变量表示棋盘大小
// 棋盘大小
private static int SIZE = 0;
修改难度选择界面三个按钮的监听事件
button1.addActionListener(e->{
SIZE = 8;
SwingUtilities.invokeLater(main::createGameGUI);
});
button2.addActionListener(e->{
SIZE = 16;
SwingUtilities.invokeLater(main::createGameGUI);
});
button3.addActionListener(e->{
SIZE = 30;
SwingUtilities.invokeLater(main::createGameGUI);
});
生成游戏界面
private void createGameGUI() {
frame = new JFrame("扫雷游戏"); // 创建游戏界面窗口
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗口关闭操作
JPanel panel = new JPanel(new GridLayout(SIZE, SIZE));
buttons = new JButton[SIZE][SIZE]; // 创建按钮二维数组,用于表示扫雷棋盘上的每个方块
// 初始化棋盘
for (int i = 1; i <= SIZE; i++) {
for (int j = 1; j <= SIZE; j++) {
buttons[i - 1][j - 1] = new JButton(); // 创建一个按钮
panel.add(buttons[i - 1][j - 1]); // 将按钮添加到面板上
}
}
frame.getContentPane().add(panel); // 将面板添加到窗口的内容面板
frame.setSize(500+SIZE*22, 500+SIZE*22); // 设置窗口大小
frame.setLocationRelativeTo(null); // 将窗口居中显示
frame.setResizable(false); // 禁止窗口大小调整
frame.setVisible(true); // 显示窗口
}
初级
中级
高级
完善游戏界面创建
完善难度选择界面三个按钮的监听事件
// 地雷数量
private static int LEI_SIZE = 0;
button1.addActionListener(e->{
SIZE = 8; // 设置棋盘大小为8x8
LEI_SIZE=10; // 设置地雷数量为10
frame.setVisible(false); // 隐藏难度选择界面
SwingUtilities.invokeLater(main::createGameGUI); // 调用创建游戏界面的方法
});
button2.addActionListener(e->{
SIZE = 16;
LEI_SIZE=35;
frame.setVisible(false);
SwingUtilities.invokeLater(main::createGameGUI);
});
button3.addActionListener(e->{
SIZE = 30;
LEI_SIZE=70;
frame.setVisible(false);
SwingUtilities.invokeLater(main::createGameGUI);
});
我们准备两个数组,一个记录棋盘是否翻开(棋盘靠这个数组显示),一个记录炸弹与非炸弹的位置
// 0 表示未翻开
private int[][] show;
// 0 表示不是雷
private int[][] lei;
完善createGameGUI()
private JButton[][] buttons;
private void createGameGUI() {
frame = new JFrame("扫雷游戏"); // 创建游戏界面窗口
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗口关闭操作
JPanel panel = new JPanel(new GridLayout(SIZE, SIZE));
buttons = new JButton[SIZE][SIZE]; // 创建按钮二维数组,用于表示扫雷棋盘上的每个方块
show = new int[SIZE+2][SIZE+2]; // 这里多加两行两列是防止后面数组越界
lei = new int[SIZE+2][SIZE+2];
COUNT = 0;
// 初始化棋盘
for (int i = 1; i <= SIZE; i++) {
for (int j = 1; j <= SIZE; j++) {
buttons[i - 1][j - 1] = new JButton(); // 创建一个按钮
// ButtonListener 自定义监听器
buttons[i - 1][j - 1].addMouseListener(new ButtonListener(i, j)); // 为按钮添加鼠标事件监听器
panel.add(buttons[i - 1][j - 1]); // 将按钮添加到面板上
}
}
gameInit(); // 初始化游戏,布雷
frame.getContentPane().add(panel); // 将面板添加到窗口的内容面板
frame.setSize(500+SIZE*22, 500+SIZE*22); // 设置窗口大小
frame.setLocationRelativeTo(null); // 将窗口居中显示
frame.setResizable(false); // 禁止窗口大小调整
frame.setVisible(true); // 显示窗口
}
实现棋盘初始化
private void gameInit() {
Random random = new Random(); // 创建一个随机数生成器对象
int count = 0; // 用于记录已布置的地雷数量
while (count < LEI_SIZE) { // 循环,直到布置足够数量的地雷
// 随机选择位置布雷
int i = random.nextInt(1000) % SIZE + 1; // 随机生成一个行号(1 到 SIZE)
int j = random.nextInt(1000) % SIZE + 1; // 随机生成一个列号(1 到 SIZE)
if (lei[i][j] != 1) { // 如果选择的位置没有地雷
lei[i][j] = 1; // 在该位置布置地雷
++count; // 增加已布置地雷数量的计数
}
}
}
自定义监听器(翻开棋盘的处理)
// 字体
private static Font font = new Font("SansSerif", Font.PLAIN, 28);
// 已翻开位置数量
private static int COUNT = 0;
// 这里继承MouseAdapter而不是实现MouseListener
// MouseAdapter 类是 MouseListener 接口的适配器类,
// 它提供了对 MouseListener 接口的默认实现,可以选择性地重写需要的方法,而无需实现所有方法。
private class ButtonListener extends MouseAdapter {
private final int row;
private final int col;
public ButtonListener(int row, int col) {
this.row = row;
this.col = col;
}
// 鼠标点击事件
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) { // 判断是否是鼠标右键点击
if (show[row][col] == 0) {
show[row][col] = 2; // 使用 'F' 表示标记
buttons[row - 1][col - 1].setFont(font); // 设置字体
buttons[row - 1][col - 1].setText("F"); // 设置按钮文本为 'F'
} else if (show[row][col] == 2) {
show[row][col] = 0; // 取消标记
buttons[row - 1][col - 1].setText(""); // 清空按钮文本
}
} else { // 非右键点击,执行原有的翻开逻辑
if (show[row][col] == 0) {
paiLei(row, col);
}
}
}
private void paiLei(int row,int col){
if (lei[row][col] == 1){ // 如果点击的位置是地雷
// 添加一个踩到地雷展示全部地雷的逻辑
showAllLei();
JOptionPane.showMessageDialog(frame, "踩到地雷,游戏结束"); // 显示游戏结束的消息框
resetGame(); // 重置游戏
resetGame(); // 重置游戏
} // 如果点击的位置不是地雷
zhanKai(row, col); // 调用展开方法继续游戏
}
//
private void showAllLei() {
for (int i = 1; i < lei.length - 1; i++) {
for (int j = 1; j < lei.length - 1; j++) {
if (lei[i][j] == 1) {
buttons[i - 1][j - 1].setEnabled(false);
buttons[i - 1][j - 1].setFont(new Font("SansSerif", Font.PLAIN, 50));
buttons[i - 1][j - 1].setText("*");
}
}
}
}
private void zhanKai(int row,int col){
// 判断是否点击的位置是地雷,或者超出了棋盘范围
if (lei[row][col] == 1 || row < 1 || row > SIZE || col < 1 || col > SIZE) return;
// 标记该位置为翻开状态
show[row][col] = 1;
// 禁用按钮,防止再次点击
buttons[row - 1][col - 1].setEnabled(false);
int count = getLeiCount(row, col); // 获取周围地雷的数量
if (count == 0) {
// 如果周围没有地雷,递归展开周围一圈的方块
for (int i = row - 1; i <= row + 1; i++) {
for (int j = col - 1; j <= col + 1; j++) {
if (show[i][j] == 0) {
zhanKai(i, j);
}
}
}
} else {
// 显示周围地雷数量,设置字体大小为 28
buttons[row - 1][col - 1].setFont(font);
buttons[row - 1][col - 1].setText(String.valueOf(count));
}
COUNT++; // 增加已翻开方块的计数
System.out.println(COUNT);
if (COUNT == (SIZE * SIZE - LEI_SIZE)) {
JOptionPane.showMessageDialog(frame, "成功排雷,游戏结束"); // 游戏胜利,显示胜利消息
resetGame(); // 重置游戏
}
}
private int getLeiCount(int row, int col) {
int count = 0;
if (lei[row - 1][col - 1] == 1) count++;
if (lei[row - 1][col] == 1) count++;
if (lei[row - 1][col + 1] == 1) count++;
if (lei[row][col - 1] == 1) count++;
if (lei[row][col + 1] == 1) count++;
if (lei[row + 1][col - 1] == 1) count++;
if (lei[row + 1][col] == 1) count++;
if (lei[row + 1][col + 1] == 1) count++;
return count;
}
}
展示
完整代码
package com.hzy.saoleGUI;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
/**
* @title: Main
* @Author zxwyhzy
* @Date: 2023/5/28 21:42
* @Version 1.0
*/
public class Main {
private JFrame frame;
private JButton[][] buttons;
// 记录位置状态 0 未翻开, 1 翻开 ,2 标记
private int[][] show;
// 记录 雷的位置 1是雷 0不是雷
private int[][] lei;
// 翻开位置个数
private static int COUNT = 0;
// 棋盘大小
private static int SIZE = 0;
// 地雷数量
private static int LEI_SIZE = 0;
private static Font font = new Font("SansSerif", Font.PLAIN, 28);
public static void main(String[] args) {
Main main = new Main();
SwingUtilities.invokeLater(() -> main.level(main));
}
private void level(Main main) {
frame = new JFrame("难度选择"); // 创建难度选择界面的窗口
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗口关闭操作
JPanel panel = new JPanel(); // 创建一个面板用于放置难度选择按钮
panel.setLayout(new GridLayout(1, 3)); // 使用GridLayout布局,将按钮排列成一行三列
JButton button1 = new JButton("初级"); // 创建初级难度按钮
button1.addActionListener(e -> {
SIZE = 8; // 设置棋盘大小为8x8
LEI_SIZE = 10; // 设置地雷数量为10
frame.setVisible(false); // 隐藏难度选择界面
SwingUtilities.invokeLater(main::createGameGUI); // 调用创建游戏界面的方法
});
panel.add(button1); // 将初级难度按钮添加到面板
JButton button2 = new JButton("中级");
button2.addActionListener(e -> {
SIZE = 16;
LEI_SIZE = 35;
frame.setVisible(false);
SwingUtilities.invokeLater(main::createGameGUI);
});
panel.add(button2);
JButton button3 = new JButton("高级");
button3.addActionListener(e -> {
SIZE = 30;
LEI_SIZE = 70;
frame.setVisible(false);
SwingUtilities.invokeLater(main::createGameGUI);
});
panel.add(button3);
frame.getContentPane().add(panel);
frame.setSize(300, 100);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void createGameGUI() {
frame = new JFrame("扫雷游戏"); // 创建游戏界面窗口
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗口关闭操作
JPanel panel = new JPanel(new GridLayout(SIZE, SIZE));
buttons = new JButton[SIZE][SIZE]; // 创建按钮二维数组,用于表示扫雷棋盘上的每个方块
show = new int[SIZE + 2][SIZE + 2]; // 这里多加两行两列是防止后面数组越界
lei = new int[SIZE + 2][SIZE + 2];
COUNT = 0;
// 初始化棋盘
for (int i = 1; i <= SIZE; i++) {
for (int j = 1; j <= SIZE; j++) {
buttons[i - 1][j - 1] = new JButton(); // 创建一个按钮
buttons[i - 1][j - 1].addMouseListener(new ButtonListener(i, j)); // 为按钮添加鼠标事件监听器
panel.add(buttons[i - 1][j - 1]); // 将按钮添加到面板上
}
}
gameInit(); // 初始化游戏,布雷
frame.getContentPane().add(panel); // 将面板添加到窗口的内容面板
frame.setSize(500 + SIZE * 22, 500 + SIZE * 22); // 设置窗口大小
frame.setLocationRelativeTo(null); // 将窗口居中显示
frame.setResizable(false); // 禁止窗口大小调整
frame.setVisible(true); // 显示窗口
}
private void resetGame() {
frame.setVisible(false);
SwingUtilities.invokeLater(() -> level(this));
}
private void gameInit() {
Random random = new Random(); // 创建一个随机数生成器对象
int count = 0; // 用于记录已布置的地雷数量
while (count < LEI_SIZE) { // 循环,直到布置足够数量的地雷
// 随机选择位置布雷
int i = random.nextInt(1000) % SIZE + 1; // 随机生成一个行号(1 到 SIZE)
int j = random.nextInt(1000) % SIZE + 1; // 随机生成一个列号(1 到 SIZE)
if (lei[i][j] != 1) { // 如果选择的位置没有地雷
lei[i][j] = 1; // 在该位置布置地雷
++count; // 增加已布置地雷数量的计数
}
}
}
// 这里继承MouseAdapter而不是实现MouseListener
// MouseAdapter 类是 MouseListener 接口的适配器类,
// 它提供了对 MouseListener 接口的默认实现,可以选择性地重写需要的方法,而无需实现所有方法。
private class ButtonListener extends MouseAdapter {
private final int row;
private final int col;
public ButtonListener(int row, int col) {
this.row = row;
this.col = col;
}
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) { // 判断是否是鼠标右键点击
if (show[row][col] == 0) {
show[row][col] = 2; // 使用 'F' 表示标记
buttons[row - 1][col - 1].setFont(font);
// buttons[row - 1][col - 1].setText("F"); // 设置按钮文本为 'F'
System.out.println("设置标记");
buttons[row - 1][col - 1].setIcon(new ImageIcon("src/com/hzy/saoleGUI/a.png"));
} else if (show[row][col] == 2) {
show[row][col] = 0; // 取消标记
// buttons[row - 1][col - 1].setText(""); // 清空按钮文本
buttons[row - 1][col - 1].setIcon(new ImageIcon(""));
System.out.println("清除标记");
}
} else { // 非右键点击,执行原有的翻开逻辑
if (show[row][col] == 0) {
paiLei(row, col);
}
}
}
private void paiLei(int row, int col) {
if (lei[row][col] == 1) { // 如果点击的位置是地雷
// 添加一个踩到地雷展示全部棋盘的逻辑
showAllLei();
JOptionPane.showMessageDialog(frame, "踩到地雷,游戏结束"); // 显示游戏结束的消息框
resetGame(); // 重置游戏
} // 如果点击的位置不是地雷
zhanKai(row, col); // 调用展开方法继续游戏
}
private void showAllLei() {
for (int i = 1; i < lei.length - 1; i++) {
for (int j = 1; j < lei.length - 1; j++) {
if (lei[i][j] == 1) {
buttons[i - 1][j - 1].setEnabled(false);
buttons[i - 1][j - 1].setFont(new Font("SansSerif", Font.PLAIN, 50));
buttons[i - 1][j - 1].setText("*");
}
}
}
}
private void zhanKai(int row, int col) {
// 判断是否点击的位置是地雷,或者超出了棋盘范围
if (lei[row][col] == 1 || row < 1 || row > SIZE || col < 1 || col > SIZE) return;
// 标记该位置为翻开状态
show[row][col] = 1;
// 禁用按钮,防止再次点击
buttons[row - 1][col - 1].setEnabled(false);
int count = getLeiCount(row, col); // 获取周围地雷的数量
if (count == 0) {
// 如果周围没有地雷,递归展开周围一圈的方块
for (int i = row - 1; i <= row + 1; i++) {
for (int j = col - 1; j <= col + 1; j++) {
if (show[i][j] == 0) {
zhanKai(i, j);
}
}
}
} else {
// 显示周围地雷数量,设置字体大小为 28
buttons[row - 1][col - 1].setFont(font);
buttons[row - 1][col - 1].setText(String.valueOf(count));
}
COUNT++; // 增加已翻开方块的计数
System.out.println(COUNT);
if (COUNT == (SIZE * SIZE - LEI_SIZE)) {
JOptionPane.showMessageDialog(frame, "成功排雷,游戏结束"); // 游戏胜利,显示胜利消息
resetGame(); // 重置游戏
}
}
private int getLeiCount(int row, int col) {
int count = 0;
if (lei[row - 1][col - 1] == 1) count++;
if (lei[row - 1][col] == 1) count++;
if (lei[row - 1][col + 1] == 1) count++;
if (lei[row][col - 1] == 1) count++;
if (lei[row][col + 1] == 1) count++;
if (lei[row + 1][col - 1] == 1) count++;
if (lei[row + 1][col] == 1) count++;
if (lei[row + 1][col + 1] == 1) count++;
return count;
}
}
}
右键插入旗帜
修改 mousePressed() 方法
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) { // 判断是否是鼠标右键点击
if (show[row][col] == 0) {
show[row][col] = 2; // 使用 'F' 表示标记
buttons[row - 1][col - 1].setFont(font);
// buttons[row - 1][col - 1].setText("F"); // 设置按钮文本为 'F'
System.out.println("设置标记");
buttons[row - 1][col - 1].setIcon(new ImageIcon("src/com/hzy/saoleGUI/a.png"));
} else if (show[row][col] == 2) {
show[row][col] = 0; // 取消标记
// buttons[row - 1][col - 1].setText(""); // 清空按钮文本
buttons[row - 1][col - 1].setIcon(new ImageIcon(""));
System.out.println("清除标记");
}
} else { // 非右键点击,执行原有的翻开逻辑
if (show[row][col] == 0) {
paiLei(row, col);
}
}
}
演示
这里随便截了张图当旗帜