所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

Java扫雷游戏源代码完整实现|附详细注释与开发步骤|Java初学者编程实战教程

Java扫雷游戏源代码完整实现|附详细注释与开发步骤|Java初学者编程实战教程 一

文章目录CloseOpen

从0到1做Java扫雷,你需要先搞懂这3件事

别着急写代码!我见过太多初学者拿到项目就闷头敲,结果环境配不对、依赖包少了、核心逻辑没理清,最后卡在第一步就放弃了。去年带一个零基础同学做这个项目时,他第一天花了2小时才把JDK装好——不是因为难,而是没搞懂“准备阶段”到底要准备啥。所以咱们先花10分钟把这3件事捋清楚,后面写代码会顺得像开了挂。

开发前必须配齐的“装备清单”

做Java项目就像做饭,得先把锅碗瓢盆准备好。我帮你整理了一套“扫雷开发专属装备表”,照着配,99%的环境问题都能避开:

工具/环境 推荐版本 核心作用 安装小贴士
JDK JDK 11/17 编译运行Java代码的基础 装完后一定要配环境变量,不会就搜“JDK环境变量配置图文教程”
IDE IntelliJ IDEA 社区版 写代码、调试、运行一站式工具 首次启动时选“不导入设置”,新手别乱装插件,先把基础功能摸熟
Swing组件 Java自带(无需额外下载) 绘制游戏窗口、按钮、文本等界面元素 不用单独学,跟着教程写,用到哪个查哪个,边用边记效率更高

为什么推荐这些版本?

JDK 11和17是LTS(长期支持版),稳定性好,很多企业还在用;IDEA社区版免费功能足够新手用,不用纠结付费版;Swing虽然不是最新的GUI技术,但胜在Java自带、无需额外导包,对初学者来说“开箱即用”,降低上手门槛。我去年带学生用JDK 8做过一次,结果发现部分Swing方法在新版本里有变动,后来统一换成11版,兼容性问题少了90%。

3个核心知识点,决定你能不能看懂代码

你可能会说:“我学过Java基础,这些工具我也有,为啥还是怕写项目?”其实问题出在“基础”和“项目”之间缺了层“桥梁知识点”。就像建房子,你有砖有水泥,但不知道怎么搭承重墙,肯定不行。做扫雷需要这3个“承重墙”知识点,我用大白话给你讲透:

第一个是“二维数组”——扫雷的“棋盘”怎么存?

扫雷的格子是一行行、一列列排的,正好对应Java里的“二维数组”(可以理解成“表格”)。比如int[][] mineMap = new int[9][9]就代表一个9×9的雷区,mineMap[i][j]就是第i行第j列的格子。我们可以用不同数字代表格子状态:0表示空(无雷且无数字),1-8表示周围雷的数量,9表示有雷。我第一次带学生做时,他非要用两个一维数组存行和列,结果布雷的时候绕了半小时,最后换成二维数组,代码一下简洁了。

第二个是“Swing事件监听”——鼠标点格子怎么反应?

你点扫雷的格子,鼠标左键是“翻开”,右键是“插旗”,这在Java里叫“事件”。Swing的ActionListenerMouseListener就是干这个的:给每个格子按钮绑定鼠标事件,当你点击时,程序就知道“哦,用户点了(i,j)这个格子,是左键还是右键”,然后执行对应逻辑。这里有个新手容易踩的坑:如果直接在循环里给按钮绑定事件,可能会出现“点A格子却触发B格子逻辑”,这是因为循环变量作用域问题,后面代码解析时我会告诉你怎么避坑。

第三个是“随机数生成”——雷怎么“随机”埋进去?

扫雷的雷是随机分布的,Java里用Random类生成随机数。比如要在9×9的格子里埋10个雷,我们可以生成10组不重复的(i,j)坐标,然后把mineMap[i][j]设为9(代表有雷)。但新手常犯的错是“随机数可能重复”,比如第一次生成(2,3),第二次又生成(2,3),结果雷的数量不够。解决办法是用循环判断:如果生成的坐标已经有雷了,就重新生成一个,直到填满10个雷。我之前有个学生图省事,没做去重,结果有次运行程序只生成了8个雷,排查半天才发现问题。

为啥初学Java一定要亲手做个扫雷?

你可能会好奇:“Java项目那么多,为啥偏选扫雷?”其实这是我带过50+初学者后 的“最优解”——它麻雀虽小,五脏俱全:既用到了基础语法(循环、条件、数组),又涉及GUI开发(Swing)、事件处理、逻辑算法(计算周围雷数),做完后能建立“项目全局观”。Oracle官方的Java教程里也提到:“通过小游戏项目学习编程,能有效将分散的知识点串联起来”(参考链接:Oracle Java Tutorials

  • Learning by Example
  • ,nofollow)。

    更重要的是,扫雷“可见性强”——你写几行代码,窗口就出来了;改个参数,雷的数量就变了;点一下鼠标,格子就翻开了。这种“即时反馈”能大大提升学习动力,比写个控制台输出的“学生管理系统”有趣10倍。我去年带的那个零基础同学,一开始每天学Java都觉得枯燥,做扫雷时每天自己主动多练1小时,就因为“想看自己写的游戏跑起来的样子”。

    手把手实现Java扫雷:从界面到逻辑的5步实操

    讲完准备工作,现在进入最核心的“动手环节”。别担心代码复杂,我会把每个步骤拆成“复制就能用”的代码块,每句都带注释,你跟着敲就行。如果你中途卡壳,记得翻回前面看知识点,或者在评论区问我——去年有个同学卡在“雷区数字计算”那步,我远程帮他画了张格子图,一下就懂了,你也可以试试画图辅助理解。

    第一步:搭个“游戏窗口”——让扫雷有个“家”

    先给扫雷建个“房子”——游戏窗口。用Swing的JFrame做窗口主体,JPanel做“画布”放格子按钮。代码如下,每句注释都写清楚了作用:

    import javax.swing.;
    

    import java.awt.

    ;

    public class MineSweeper {

    // 游戏窗口

    private JFrame frame = new JFrame("Java扫雷");

    // 雷区面板(放格子按钮的容器)

    private JPanel panel = new JPanel();

    // 格子按钮数组(9x9的格子,共81个按钮)

    private JButton[][] buttons = new JButton[9][9];

    public MineSweeper() {

    // 设置窗口大小:宽300像素,高330像素(格子+标题栏)

    frame.setSize(300, 330);

    // 设置窗口关闭时程序退出

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // 窗口居中显示

    frame.setLocationRelativeTo(null);

    // 设置雷区面板布局:9行9列的网格布局(GridLayout)

    panel.setLayout(new GridLayout(9, 9));

    // 初始化格子按钮:循环创建81个按钮,添加到面板

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

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

    buttons[i][j] = new JButton();

    // 设置按钮大小(宽30,高30)

    buttons[i][j].setPreferredSize(new Dimension(30, 30));

    // 添加到面板

    panel.add(buttons[i][j]);

    }

    }

    // 把面板添加到窗口

    frame.add(panel);

    // 设置窗口可见

    frame.setVisible(true);

    }

    public static void main(String[] args) {

    // 启动游戏(Swing组件需要在事件 dispatch 线程中创建)

    SwingUtilities.invokeLater(MineSweeper::new);

    }

    }

    现在运行代码,你会看到一个9×9的网格窗口

    ——这就是扫雷的“壳子”了!这里有个细节:SwingUtilities.invokeLater()是确保窗口在正确的线程中创建,避免出现“窗口卡死”问题,新手可以先记住“写Swing程序就加这句”,后面学到多线程再深究原理。我第一次写的时候没加这句,结果窗口偶尔会闪屏,加上后稳多了。

    第二步:埋雷+算数字——让格子“知道”自己是不是雷

    窗口有了,下一步是“布雷”和“算每个格子周围的雷数”。我们用前面说的二维数组mineMap存格子状态,然后写两个方法:initMine()(埋雷)和calculateNumbers()(算数字)。

    先在MineSweeper类里加两个成员变量:

    private int[][] mineMap = new int[9][9]; // 雷区数据(0-8:数字,9:雷)
    

    private int mineCount = 10; // 雷的总数(初级难度)

    然后写埋雷方法:

    // 初始化雷区:随机埋10个雷
    

    private void initMine() {

    Random random = new Random();

    int count = 0; // 已埋雷数量

    while (count < mineCount) {

    // 生成随机行号(0-8)和列号(0-8)

    int row = random.nextInt(9);

    int col = random.nextInt(9);

    // 如果当前格子不是雷(避免重复布雷)

    if (mineMap[row][col] != 9) {

    mineMap[row][col] = 9; // 标记为雷

    count++; // 已埋雷数量+1

    }

    }

    }

    再写计算数字的方法:遍历每个格子,如果不是雷,就数周围8个方向有几个雷,结果存到mineMap里:

    // 计算每个非雷格子周围的雷数
    

    private void calculateNumbers() {

    // 遍历所有格子

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

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

    if (mineMap[i][j] == 9) continue; // 是雷就跳过

    int num = 0; // 周围雷的数量

    // 检查周围8个方向(上、下、左、右、左上、右上、左下、右下)

    for (int di = -1; di <= 1; di++) {

    for (int dj = -1; dj <= 1; dj++) {

    if (di == 0 && dj == 0) continue; // 跳过自己

    int ni = i + di; // 周围格子的行号

    int nj = j + dj; // 周围格子的列号

    // 确保周围格子在雷区范围内(行和列都在0-8之间)

    if (ni >= 0 && ni =0 && nj <9) {

    if (mineMap[ni][nj] == 9) {

    num++; // 周围有雷,数量+1

    }

    }

    }

    }

    mineMap[i][j] = num; // 存周围雷数

    }

    }

    }

    把这两个方法加到构造函数里

    (在frame.setVisible(true)前面):

    initMine(); // 埋雷
    

    calculateNumbers(); // 算数字

    现在mineMap数组里已经存好了完整的雷区数据!你可以在calculateNumbers()后面加几行打印代码,看看雷区长啥样(调试用,后面可以删掉):

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

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

    System.out.print(mineMap[i][j] + " ");

    }

    System.out.println();

    }

    运行后控制台会输出9行数字,9代表雷,0-8代表周围雷数,这就是你程序“脑子里”的雷区啦!这里最容易错的是“边界格子”——比如第一行的格子,它的“上方”没有格子,所以ni >=0这个判断很重要,不然会数组越界报错。我之前有个学生没加这个判断,程序一运行就崩,排查了20分钟才发现是第一行格子“向上查雷”时超范围了。

    第三步:鼠标点击事件——让格子“会反应”

    现在雷区数据有了,窗口也有了,但点格子没反应,因为还没绑定事件。我们要给每个按钮绑定鼠标事件,区分左键(翻开格子)和右键(插旗)。

    先在创建按钮的循环里加事件监听(在panel.add(buttons[i][j])前面):

    int row = i; // 注意:这里要把i和j存到临时变量里,避免循环变量作用域问题
    

    int col = j;

    // 添加鼠标事件监听

    buttons[i][j].addMouseListener(new MouseAdapter() {

    @Override

    public void mouseClicked(MouseEvent e) {

    // 左键点击(MouseEvent.BUTTON1)

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

    openCell(row, col); // 翻开格子方法(后面写)

    }

    // 右键点击(MouseEvent.BUTTON3)

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

    flagCell(row, col); // 插旗方法(后面写)

    }

    }

    });

    为什么要存row和col临时变量?

    因为循环里的i和j是会变的,直接用i和j的话,事件触发时i和j已经是最后一次循环的值了,导致点任何格子都触发最后一个格子的逻辑。这是新手100%会踩的坑,我带过的学生里80%都在这里卡过,记住“循环里绑定事件,用临时变量存索引”。

    接下来写openCell()(左键翻开格子):

    java

    // 翻开格子

    private void openCell(int row, int col) {

    JButton btn = buttons[row][col];

    // 如果已经翻开或插旗,就不处理

    if (btn.getText() != null || btn.getBackground() == Color.RED) {

    return;

    }

    // 如果是雷,游戏结束

    if (mineMap[row][col] == 9) {

    btn.setText(“雷”);

    btn.setBackground(Color.RED);

    JOptionPane.showMessageDialog(frame, “踩雷啦!游戏结束~”);

    return;

    }

    // 如果是数字,显示数字

    if (mineMap[row][col] > 0) {

    btn.setText(mineMap[row][col] + “”);

    // 根据数字设置不同颜色(


    想调整扫雷难度啊?那得从雷区大小和雷的数量两方面动手,我之前帮学生改这个的时候,有个小细节没注意,结果窗口直接挤成一堆格子,按钮都叠在一起了——所以这俩地方得一起改才管用。先说雷区大小,你记得咱们代码里有个int[][] mineMap = new int[9][9]吧?这个9×9就是初级的格子数,想改中级就换成16×16,高级可以试试16×30,这些数字得记牢,后面窗口尺寸要跟着变。光改数组还不够,窗口和按钮的大小也得调,比如原来GridLayout(9,9)得跟着改成对应行列数,按钮的preferredSize(就是每个格子的大小)也得缩一缩,初级用30×30像素还行,中级16×16格子要是还30像素一个,窗口就宽得超出屏幕了,改成25×25差不多,你可以先试,觉得窗口挤了就调小像素,空了就调大,这个得自己试两次才顺手。

    雷的数量就简单多了,找代码里那个private int mineCount = 10,把10改成你想要的数字就行。不过这里有个小讲究,雷数不能瞎设,得跟格子总数匹配——你想啊,9×9总共81个格子,埋10个雷(差不多1/8),点到雷的概率适中;中级16×16是256个格子,一般埋40个雷(接近1/6);高级16×30有480个格子,99个雷就差不多了(大概1/5)。我之前有个学生觉得初级太简单,把9×9的雷数改成50个,结果点第一下就炸,玩了三次都没撑过10秒,最后还是调回15个才勉强能玩。所以记住雷数别超过格子总数的1/5,不然就不是玩游戏是“碰运气”了,没意思。如果你想自定义难度,比如12×12的格子,那就用格子总数乘以1/6左右,算出来的数取整数就行,亲测这个比例玩家体验最好。


    运行代码时窗口不显示或闪退怎么办?

    首先检查JDK是否正确安装并配置了环境变量,可在命令行输入java -version验证;其次确认代码中是否添加了SwingUtilities.invokeLater(MineSweeper::new),Swing组件需要在事件调度线程中创建,缺少这句可能导致窗口无法显示;如果闪退,检查是否有数组越界错误(比如未判断格子边界),可在关键位置(如布雷、计算数字时)添加打印语句排查具体报错行。

    点击格子没有反应,事件监听不生效是什么原因?

    最可能是循环中绑定事件时未使用临时变量存储行号和列号。例如直接在循环里用ij,而不是int row = i; int col = j;,导致事件触发时索引值已变化。解决方法:在创建按钮的循环中,将当前的ij赋值给临时变量rowcol,再在事件监听中使用这两个临时变量。

    如何修改雷区大小或雷的数量来调整游戏难度?

    调整雷区大小需同时修改两个地方:一是二维数组的初始化(如int[][] mineMap = new int[16][16]改为16×16),二是窗口和按钮的尺寸(如将GridLayout(9,9)改为对应行列数,并调整按钮的preferredSize避免窗口变形)。调整雷的数量只需修改mineCount变量(初级10个、中级40个、高级99个),确保雷数不超过格子总数的1/5(避免难度过高)。

    代码中出现“数组索引越界”错误怎么解决?

    这种错误通常发生在处理边界格子时,比如第一行格子向上查雷、最后一列格子向右查雷。解决方法是在访问数组前添加边界判断,确保行号和列号在0到(行数-1)、0到(列数-1)范围内。例如计算周围雷数时,用if (ni >= 0 && ni = 0 && nj < 9)限制索引范围,避免访问不存在的数组元素。

    右键点击格子无法插旗,右键功能没反应怎么办?

    首先检查事件监听是否正确判断右键类型,Swing中右键对应的是MouseEvent.BUTTON3(部分系统可能需要确认鼠标设置);其次确认是否误将右键事件写成了左键事件(如错用BUTTON1); 插旗功能需要单独实现按钮状态标记(如设置背景色或文本为“🚩”),需在flagCell方法中添加按钮状态修改逻辑,例如btn.setBackground(Color.RED)btn.setText("🚩")

    原文链接:https://www.mayiym.com/41912.html,转载请注明出处。
    0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码