这是一篇关于JAVA的扫雷游戏,所有的图片均用文字代替,代码可直接运行。

文章目录

开发环境一、下载方法二、运行效果展示三、代码部分1.代码如下

总结

开发环境

开发工具:eclipse2021-12 JDK版本:JDK15.0.1

一、下载方法

链接:https://pan.baidu.com/s/1pw4WwztyfWrlSq_V2y6jfA 提取码:dhpm

二、运行效果展示

这张图是游戏刚开始的画面,重置以后也是这个画面 此图是写代码的过程调试用的画面,方便查找问题。

此图是运行过程中的图片 带有计时功能,游戏成功的条件是用完所有的旗,并且放在雷上面。 超时,或者用完旗以后,有旗子没在正确的位置上,或者踩到地雷,都会导致游戏失败。

三、代码部分

1.代码如下

代码如下(示例):

package com.first;

import java.awt.Color;

import java.awt.Insets;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.awt.event.MouseAdapter;

import java.awt.event.MouseEvent;

import javax.swing.BorderFactory;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JOptionPane;

import javax.swing.JPanel;

public class MineSweeper {

static private GamePanel gPanel;/* 雷区 */

static private int midtime = 3600, mineNum = 0;/* 倒计时时间以及可用旗子数量 */

static private JLabel label1, label2;

static CountDown cd;

/**

* 构造函数 用于初始化

*/

public MineSweeper() {

JFrame jf = new JFrame("扫雷小游戏");

jf.setDefaultCloseOperation(3);// EXIT_ON_CLOSE,直接关闭应用程序,System.exit(0)。一个main函数对应一整个程序。

jf.setLayout(null);// 未设置Layout时,java默认为flowLayout布局的,设置为null即为清空布局管理器,之后添加组件,常常是设置组件左上角坐标相对于容器左上角(0,0)的x,y值来确定组件的位置

jf.setBounds(600, 200, 700, 800);// 设置窗口的大小和位置

label1 = new JLabel("剩余时间:" + (midtime / 60 / 60 % 60) + ":" + (midtime / 60 % 60) + ":" + (midtime % 60));

label1.setBounds(10, 20, 120, 20);

jf.add(label1);

label2 = new JLabel("剩余旗子:" + mineNum);

label2.setBounds(400, 20, 240, 20);// 设置label2的位置以及大小

jf.add(label2);

JButton resetButt = new JButton("重置");// 创建重置按钮

resetButt.setMargin(new Insets(0, 0, 0, 0));// 设置边框

resetButt.setBounds(230, 15, 60, 30);// 设置按钮在窗口中的位置和大小

resetButt.addActionListener(new ActionListener() {

@Override

public void actionPerformed(ActionEvent e) {

// TODO Auto-generated method stub

jf.dispose();/* 销毁窗口 */

new MineSweeper();

midtime = 3600;

}

});

jf.add(resetButt);// 把按钮添加到窗口中

gPanel = new GamePanel(20, 20);// 设置按钮数量为20行*20列

gPanel.setBounds(40, 100, 600, 600);// 设置雷区面板的位置以及大小

jf.add(gPanel);// 雷区面板添加到窗口中

jf.setVisible(true);// 设置窗口可见

}

static class CountDown extends Thread {

@Override

public void run() {

super.run();

while (midtime > 0) {

try {

--midtime;

label1.setText(

"剩余时间:" + (midtime / 60 / 60 % 60) + ":" + (midtime / 60 % 60) + ":" + (midtime % 60));

sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

if (midtime == 0) {

JOptionPane.showMessageDialog(null, "时间已到", "游戏结束", JOptionPane.PLAIN_MESSAGE);

}

}

}

public static void main(String[] args) {// 程序的入口

new MineSweeper();// 进入构造函数

cd = new CountDown();// 创建计时器

cd.start();/* 启动计时器 */

}

public static void setMineNum(int minesCount2) {

// TODO Auto-generated method stub

mineNum = minesCount2;

label2.setText("剩余旗子:" + mineNum);

}

}

class GamePanel extends JPanel {

private int rows, cols, minesCount;// 创建行数、列数变量,地雷的数量

private final int BLOCKWIDTH = 30;// 规定每个按钮的宽度

private final int BLOCKHEIGHT = 30;// 规定每个按钮的高度

private Buts[][] buts;// 雷区按钮

private JLabel[][] jlabel;// 每个按钮下对应标签

private boolean[][] state;// true:有雷;false:没有雷

private int[][] click;// 0:未点击,1:已点击,2:未点击但周围有雷,3:插旗

public GamePanel(int row, int col) {

rows = row;// 行数

cols = col;// 列数

minesCount = rows * cols / 10;// 地雷的数量

MineSweeper.setMineNum(minesCount);

buts = new Buts[rows][cols];// 创建雷区按钮

jlabel = new JLabel[rows][cols];// 创建标签

state = new boolean[rows][cols];// true:有雷;false:没有雷

click = new int[rows][cols];// 0:未点击,1:已点击,2:未点击但周围有雷,3:插旗

setLayout(null);// 设置为null即为清空布局管理器,之后添加组件,常常是设置组件左上角坐标相对于容器左上角(0,0)的x,y值来确定组件的位置

initButtons();// 初始化按钮,设置每个按钮的基本参数

initLable();// 初始化标签方格,设置每个标签的基本参数

buryMines();// 埋雷,随机生成地雷

writerNumber();// 计算每个方格周围8个方格中有几个地雷

}

/* 用于统计每个方格周围有多少地雷 */

private void writerNumber() {

for (int i = 0; i < rows; i++) {

for (int j = 0; j < cols; j++) {

if (!state[i][j]) {// 没有雷的地方才探测周围的情况

int numbers = 0;// 用于记录每个方格四周有多少地雷

for (int r = -1; r < 2; r++) {// 探测每个方格行坐标-1,0,+1后的情况

for (int c = -1; c < 2; c++) {// 探测每个方格列坐标-1,0,+1后的情况

if ((i + r > -1) && (i + r < cols) && (j + c > -1) && (j + c < rows)) {// 判断有没有超过数组的边界

if (state[i + r][j + c] == true) {// 探测的位置上有地雷则numbers加1

numbers++;

}

}

}

}

if (numbers > 0) {

click[i][j] = 2;// 设置为:未点击但周围有雷

}

jlabel[i][j].setText(String.valueOf(numbers));// 给每个单元赋值,周围8个方格中有几个地雷

// buts[i][j].setText(String.valueOf(numbers));// 调试代码用

}

}

}

}

private void initButtons() {

for (int i = 0; i < rows; i++) {

for (int j = 0; j < cols; j++) {

Buts but = new Buts();

but.i = i;// 给按钮的行信息赋值

but.j = j;// 给按钮的列信息赋值

but.setBounds(BLOCKWIDTH * j, BLOCKHEIGHT * i, BLOCKWIDTH, BLOCKHEIGHT);// 设置每一个按钮的位置和大小

but.setMargin(new Insets(0, 0, 0, 0));// 设置按钮边框

/* 给按钮添加鼠标监听器,检测是否点击 */

but.addMouseListener(new MouseAdapter() {

@Override

public void mouseClicked(MouseEvent e) {

/* 左击 */

if (e.getButton() == MouseEvent.BUTTON1) {

// but.setVisible(false);//调试代码用

// jlabel[but.i][but.j].setVisible(true);//调试代码用

openButtons(but);

}

/* 右击 */

if (e.getButton() == MouseEvent.BUTTON3) {

// but.setText("旗");// 调试代码用

placeFlag(but);

}

}

});

buts[i][j] = but;// 赋值给公共变量,方便调用

this.add(buts[i][j]);// 添加到面板中jpanel

}

}

}

protected void placeFlag(Buts but) {

if (click[but.i][but.j] != 3) {

buts[but.i][but.j].setText("旗");

// System.out.println("click[but.i][but.j] =" + click[but.i][but.j]);// 调试代码用

click[but.i][but.j] = 3;// 0:未点击,1:已点击,2:未点击但周围有雷,3:插旗

minesCount--;

MineSweeper.setMineNum(minesCount);

} else {

buts[but.i][but.j].setText("");

if (!state[but.i][but.j]) {

if (Integer.valueOf(jlabel[but.i][but.j].getText()) > 0) {

click[but.i][but.j] = 2;// 周围有地雷

// System.out.println("click[but.i][but.j] =" + click[but.i][but.j]);// 调试代码用

} else {

click[but.i][but.j] = 0;// 未点击

// System.out.println("click[but.i][but.j] =" + click[but.i][but.j]);// 调试代码用

}

minesCount++;

MineSweeper.setMineNum(minesCount);

} else {

click[but.i][but.j] = 0;// 未点击

}

}

if (minesCount == 0) {// 当旗子用完后,判断是否全部标记正确

boolean flag = true;

for (int i = 0; i < rows; i++) {

for (int j = 0; j < cols; j++) {

if ((click[i][j] != 3) && (state[i][j])) {// 对所有没有插旗的方格进行判断,如果没有插旗,但是state的状态又未true,则令flag未false

flag = false;

}

}

}

if (flag) {

MineSweeper.cd.stop();// 停止计时

JOptionPane.showMessageDialog(null, "您成功了", "游戏结束", JOptionPane.PLAIN_MESSAGE);

} else {

MineSweeper.cd.stop();// 停止计时

JOptionPane.showMessageDialog(null, "旗子已经用完", "游戏失败", JOptionPane.PLAIN_MESSAGE);

}

}

}

protected void openButtons(Buts but) {

int i = but.i;

int j = but.j;

if (state[i][j] && (click[i][j] != 3)) {

openAllCell();

MineSweeper.cd.stop();

JOptionPane.showMessageDialog(null, "您踩到地雷了", "游戏结束", JOptionPane.PLAIN_MESSAGE);

}

if (click[i][j] == 2) {

buts[i][j].setVisible(false);

jlabel[i][j].setVisible(true);

}

if ((click[i][j] == 0) || (click[i][j] == 1)) {// 0的周围是要判断的,1的周围也有可能所以也要判断

for (int r = -1; r < 2; r++) {

for (int c = -1; c < 2; c++) {

if ((i + r > -1) && (i + r < cols) && (j + c > -1) && (j + c < rows) && (!state[i + r][j + c])) {// 判断有没有超过数组的边界

if (click[i + r][j + c] == 0) {// 0:未点击,1:已点击,2:未点击但周围有雷,3:插旗

buts[i + r][j + c].setVisible(false);

jlabel[i + r][j + c].setVisible(true);

click[i + r][j + c] = 1;

openButtons(buts[i + r][j + c]);

}

if (click[i + r][j + c] == 2) {

buts[i + r][j + c].setVisible(false);

jlabel[i + r][j + c].setVisible(true);

}

}

}

}

}

}

private void openAllCell() {

// TODO Auto-generated method stub

for (int i = 0; i < rows; i++) {

for (int j = 0; j < cols; j++) {

buts[i][j].setVisible(false);

jlabel[i][j].setVisible(true);

click[i][j] = 1;

}

}

}

private void initLable() {

for (int i = 0; i < rows; i++) {

for (int j = 0; j < cols; j++) {

JLabel l = new JLabel("", JLabel.CENTER);

l.setBounds(j * BLOCKWIDTH, i * BLOCKHEIGHT, BLOCKWIDTH, BLOCKHEIGHT);// 设置每个小方格的位置大小

l.setBorder(BorderFactory.createLineBorder(Color.gray));// 绘制方格边框为灰色

l.setOpaque(true);// 设置方格为透明,方便我们填充

l.setBackground(Color.lightGray);// 背景填充为浅灰色

this.add(l);// 添加到面板中

jlabel[i][j] = l;// 将方格加到类变量中,方便公用

l.setVisible(false);// 设置不可见

}

}

}

private void buryMines() {// 生成雷区

for (int i = 0; i < minesCount; i++) {

int x = (int) (Math.random() * rows);// 生成随机行数

int y = (int) (Math.random() * cols);// 生成随机列数

/* 判断随机生成的位置是不是有地雷 */

if (state[x][y] == true) {// 如果随机生成的位置上已经有雷了,则i--

i--;

} else {// 位置上没有雷,则state为true,标签设置为雷

state[x][y] = true;

jlabel[x][y].setText("雷");

// buts[x][y].setText("雷");// 调式代码用

// System.out.println(x + "," + y + ":位置有地雷");//调式代码用

}

}

}

}

class Buts extends JButton {

public int i, j;

}

总结

不到300行代码实现了扫雷游戏,效果也不错。整个游戏经过几轮的优化,整体性能不错,方便理解。是上手实战的好项目。 附上下载连接地址: 链接:https://pan.baidu.com/s/1pw4WwztyfWrlSq_V2y6jfA 提取码:dhpm 欢迎交流,共同进步。

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: