跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

Java 拼图小游戏开发:基于 Swing 的完整实现

Java 拼图小游戏基于 Swing 框架开发,涵盖界面搭建、图片分割加载、随机打乱算法及鼠标交互逻辑。项目包含计时与步数统计功能,通过坐标计算判断移动合法性与胜利条件。代码结构清晰,适合初学者巩固 GUI 编程与事件处理知识,支持扩展难度选择与动画效果。

魔法巫师发布于 2026/3/26更新于 2026/6/1119 浏览
Java 拼图小游戏开发:基于 Swing 的完整实现

一、项目设计与准备工作

1.1 功能定位

这款拼图游戏基于经典的数字拼图玩法,将一张图片分割为 N×N 的方块(以 3×3 为例),随机打乱后通过点击或拖拽实现方块移动,最终还原为完整图片。

核心功能包括:

  • 图片分割与加载
  • 随机打乱算法
  • 鼠标交互控制
  • 游戏胜利判断
  • 计时与步数统计
1.2 开发环境
  • JDK 1.8 及以上
  • IDE:IntelliJ IDEA(或 Eclipse)
  • 技术栈:Swing(Java 自带 GUI 库,无需额外依赖)
1.3 项目结构
PuzzleGame/
├─ src/
│  ├─ Main.java // 程序入口
│  ├─ PuzzleFrame.java // 主窗口类
│  └─ ImageUtil.java // 图片处理工具类
└─ images/ // 存放游戏图片

二、基础界面搭建(Step 1)

首先创建主窗口框架,使用 Swing 的 JFrame 作为容器,设置基本属性并添加菜单组件。

// PuzzleFrame.java
import javax.swing.*;
import java.awt.*;

public class PuzzleFrame extends JFrame {
    // 游戏参数
    private static final int SIZE = 3; // 3×3 拼图
    private static final int BLOCK_SIZE = 150; 
     [][] data =  [SIZE][SIZE]; 

      {
        initFrame();
        initMenu();
        initData();
        setVisible();
    }

    
       {
        setTitle();
        setSize(SIZE * BLOCK_SIZE + , SIZE * BLOCK_SIZE + );
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(); 
        setLayout(); 
    }

    
       {
            ();
            ();
            ();
            ();
        gameMenu.add(restartItem);
        gameMenu.add(exitItem);
        menuBar.add(gameMenu);
        setJMenuBar(menuBar);
        
        exitItem.addActionListener(e -> System.exit());
    }

    
       {
         (   ; i < SIZE; i++) {
             (   ; j < SIZE; j++) {
                data[i][j] = i * SIZE + j + ;
            }
        }
        data[SIZE - ][SIZE - ] = ; 
    }

        {
         ();
    }
}
// 每个方块大小
private
int
new
int
// 存储方块编号
public
PuzzleFrame
()
true
// 初始化窗口属性
private
void
initFrame
()
"Java 拼图游戏"
50
100
null
// 居中显示
null
// 绝对布局,方便控制方块位置
// 初始化菜单
private
void
initMenu
()
JMenuBar
menuBar
=
new
JMenuBar
JMenu
gameMenu
=
new
JMenu
"游戏"
JMenuItem
restartItem
=
new
JMenuItem
"重新开始"
JMenuItem
exitItem
=
new
JMenuItem
"退出"
// 退出功能
0
// 初始化数据(1-8 为方块,0 为空位)
private
void
initData
()
for
int
i
=
0
for
int
j
=
0
1
1
1
0
// 右下角为空位
public
static
void
main
(String[] args)
new
PuzzleFrame

关键知识点:

  • JFrame 作为顶层容器,负责窗口基本属性配置
  • JMenuBar、JMenu、JMenuItem 组合实现菜单功能
  • 绝对布局(null layout)便于精确控制组件位置

三、图片加载与分割(Step 2)

接下来实现图片处理功能,将原图分割为对应数量的方块并加载显示。

// ImageUtil.java
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class ImageUtil {
    // 分割图片为 SIZE×SIZE 的小方块
    public static BufferedImage[] splitImage(String path, int size, int blockSize) {
        try {
            BufferedImage srcImage = ImageIO.read(new File(path));
            // 缩放原图以适应游戏窗口
            Image scaledImage = srcImage.getScaledInstance(size * blockSize, size * blockSize, Image.SCALE_SMOOTH);
            BufferedImage destImage = new BufferedImage(size * blockSize, size * blockSize, BufferedImage.TYPE_INT_RGB);
            destImage.getGraphics().drawImage(scaledImage, 0, 0, null);
            // 分割图片
            BufferedImage[] blocks = new BufferedImage[size * size];
            for (int i = 0; i < size; i++) {
                for (int j = 0; j < size; j++) {
                    int index = i * size + j;
                    blocks[index] = destImage.getSubimage(j * blockSize, i * blockSize, blockSize, blockSize);
                }
            }
            return blocks;
        } catch (IOException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "图片加载失败!");
            return null;
        }
    }
}

在 PuzzleFrame 中添加图片加载与绘制逻辑:

// 在 PuzzleFrame 中添加成员变量
private BufferedImage[] imageBlocks;
private int emptyRow = SIZE - 1; // 空位行坐标
private int emptyCol = SIZE - 1; // 空位列坐标

// 初始化图片
private void initImage() {
    imageBlocks = ImageUtil.splitImage("images/pic.jpg", SIZE, BLOCK_SIZE);
}

// 重写 paint 方法绘制界面
@Override
public void paint(Graphics g) {
    super.paint(g);
    // 绘制游戏区域边框
    g.setColor(Color.GRAY);
    g.fillRect(20, 50, SIZE * BLOCK_SIZE, SIZE * BLOCK_SIZE);
    // 绘制方块
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            int value = data[i][j];
            if (value != 0) { // 非空位绘制图片
                g.drawImage(imageBlocks[value - 1], j * BLOCK_SIZE + 20, i * BLOCK_SIZE + 50, BLOCK_SIZE, BLOCK_SIZE, null);
            }
        }
    }
    // 绘制网格线
    for (int i = 0; i <= SIZE; i++) {
        g.setColor(Color.WHITE);
        g.drawLine(20, 50 + i * BLOCK_SIZE, 20 + SIZE * BLOCK_SIZE, 50 + i * BLOCK_SIZE);
        g.drawLine(20 + i * BLOCK_SIZE, 50, 20 + i * BLOCK_SIZE, 50 + SIZE * BLOCK_SIZE);
    }
}

开发要点:

  1. 需在项目根目录创建 images 文件夹并放入 pic.jpg 图片
  2. BufferedImage 类用于图片处理,getSubimage 实现分割
  3. 重写 paint 方法实现自定义绘制,注意绘制顺序(先背景后元素)

四、核心逻辑实现(Step 3)

4.1 打乱算法

采用随机交换法实现打乱,但需保证拼图可解(3×3 拼图需满足逆序数为偶数):

// 打乱方块
private void shuffle() {
    int count = 0; // 随机交换 100 次
    for (int i = 0; i < 100; i++) {
        int dir = (int) (Math.random() * 4); // 0-3 代表上下左右
        switch (dir) {
            case 0: // 上
                if (emptyRow > 0) {
                    swap(emptyRow, emptyCol, emptyRow - 1, emptyCol);
                    emptyRow--;
                }
                break;
            case 1: // 下
                if (emptyRow < SIZE - 1) {
                    swap(emptyRow, emptyCol, emptyRow + 1, emptyCol);
                    emptyRow++;
                }
                break;
            case 2: // 左
                if (emptyCol > 0) {
                    swap(emptyRow, emptyCol, emptyRow, emptyCol - 1);
                    emptyCol--;
                }
                break;
            case 3: // 右
                if (emptyCol < SIZE - 1) {
                    swap(emptyRow, emptyCol, emptyRow, emptyCol + 1);
                    emptyCol++;
                }
                break;
        }
    }
}

// 交换两个位置的元素
private void swap(int r1, int c1, int r2, int c2) {
    int temp = data[r1][c1];
    data[r1][c1] = data[r2][c2];
    data[r2][c2] = temp;
}
4.2 鼠标交互

添加鼠标监听器实现点击移动功能:

// 初始化鼠标监听
private void initMouseListener() {
    addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            int x = e.getX();
            int y = e.getY();
            // 判断点击位置是否在游戏区域内
            if (x >= 20 && x <= 20 + SIZE * BLOCK_SIZE && y >= 50 && y <= 50 + SIZE * BLOCK_SIZE) {
                // 计算点击的方块坐标
                int clickRow = (y - 50) / BLOCK_SIZE;
                int clickCol = (x - 20) / BLOCK_SIZE;
                // 判断是否可移动(相邻空位)
                if ((Math.abs(clickRow - emptyRow) == 1 && clickCol == emptyCol) || (Math.abs(clickCol - emptyCol) == 1 && clickRow == emptyRow)) {
                    // 交换位置
                    swap(clickRow, clickCol, emptyRow, emptyCol);
                    // 更新空位坐标
                    emptyRow = clickRow;
                    emptyCol = clickCol;
                    // 重绘界面
                    repaint();
                    // 判断是否胜利
                    if (checkWin()) {
                        JOptionPane.showMessageDialog(PuzzleFrame.this, "恭喜完成拼图!");
                    }
                }
            }
        }
    });
}

// 胜利判断
private boolean checkWin() {
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            // 最后一个位置应为 0
            if (i == SIZE - 1 && j == SIZE - 1) {
                if (data[i][j] != 0) return false;
            } else {
                if (data[i][j] != i * SIZE + j + 1) return false;
            }
        }
    }
    return true;
}

在构造方法中添加初始化调用:

public PuzzleFrame() {
    initFrame();
    initMenu();
    initData();
    initImage();
    initMouseListener();
    shuffle(); // 启动时打乱
    setVisible(true);
}

核心算法解析:

  • 打乱采用模拟人玩的随机移动法,保证可解性
  • 鼠标点击通过坐标计算确定目标方块,仅允许相邻空位移动
  • 胜利判断通过对比当前状态与目标状态实现

五、功能完善与优化(Step 4)

5.1 计时与步数统计

添加计时功能和步数统计,提升游戏体验:

// 添加成员变量
private int stepCount = 0; // 步数
private long startTime; // 开始时间
private JLabel timeLabel = new JLabel("时间:0 秒");
private JLabel stepLabel = new JLabel("步数:0");

// 在 initFrame 中添加统计标签
private void initFrame() {
    // ... 原有代码 ...
    // 添加统计面板
    JPanel infoPanel = new JPanel();
    infoPanel.setBounds(20, 10, SIZE * BLOCK_SIZE, 30);
    infoPanel.add(timeLabel);
    infoPanel.add(stepLabel);
    add(infoPanel);
    // 初始化计时
    startTime = System.currentTimeMillis();
    new Timer(1000, e -> {
        long time = (System.currentTimeMillis() - startTime) / 1000;
        timeLabel.setText("时间:" + time + "秒");
    }).start();
}

// 移动后更新步数(在 mouseClicked 中)
stepCount++;
stepLabel.setText("步数:" + stepCount);

// 重新开始功能(在菜单监听器中)
restartItem.addActionListener(e -> {
    initData();
    shuffle();
    stepCount = 0;
    stepLabel.setText("步数:0");
    startTime = System.currentTimeMillis();
    repaint();
});
5.2 界面美化

优化视觉效果,添加游戏标题和背景:

// 重写 paint 方法时添加标题绘制
g.setColor(Color.BLUE);
g.setFont(new Font("宋体", Font.BOLD, 20));
g.drawString("Java 拼图游戏", 20, 35);
// 设置窗口背景
setBackground(Color.LIGHT_GRAY);

六、项目总结与拓展方向

6.1 开发收获

通过本项目实践,掌握了:

  • Swing 组件的使用与布局管理
  • 图片处理与自定义绘制
  • 事件驱动编程与用户交互
  • 游戏逻辑设计与算法实现
6.2 拓展建议
  1. 增加难度选择(4×4、5×5)
  2. 实现拖拽移动功能
  3. 添加图片选择功能
  4. 记录最佳成绩排行榜
  5. 实现动画过渡效果
6.3 完整代码结构

最终项目包含三个核心类,共约 300 行代码,实现了一个功能完整、交互友好的拼图游戏。通过这个项目,不仅能巩固 Java 基础知识,更能理解小型应用的开发流程。

目录

  1. 一、项目设计与准备工作
  2. 1.1 功能定位
  3. 1.2 开发环境
  4. 1.3 项目结构
  5. 二、基础界面搭建(Step 1)
  6. 三、图片加载与分割(Step 2)
  7. 四、核心逻辑实现(Step 3)
  8. 4.1 打乱算法
  9. 4.2 鼠标交互
  10. 五、功能完善与优化(Step 4)
  11. 5.1 计时与步数统计
  12. 5.2 界面美化
  13. 六、项目总结与拓展方向
  14. 6.1 开发收获
  15. 6.2 拓展建议
  16. 6.3 完整代码结构
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Vue 下拉刷新组件开发实战与 Slot 用法详解
  • Z-Image-Turbo WebUI 本地部署与实战指南
  • 逻辑回归算法详解:原理、代码与可视化
  • Science Robotics 新突破:注意力机制结合强化学习实现足式机器人障碍穿越
  • Visual C++ 运行库修复工具使用指南
  • Git LFS 跨平台安装指南:Linux、macOS 与 Windows 配置详解
  • 前端大数据导出优化:解决 Chrome 内存崩溃的实战方案
  • Python 技术副业实战指南:从入门学习到数据变现路径
  • C 语言 Web 开发:CGI、FastCGI、Nginx 深度解析
  • 蓝桥杯算法实战指南:递推、递归、BFS 与 DFS 详解
  • 双指针算法:四数之和解题思路与实现
  • 医疗 AI 中的模型融合与集成策略实战
  • VS Code 前端开发 AI 工具对比:Copilot、通义灵码、iFlyCode 与 Trae
  • 本地运行 AI 大模型指南:Ollama 快速上手
  • Python 网站爬虫核心技术栈与实战指南
  • JDK 安装和环境配置教程
  • 滑动窗口算法核心思路与四道经典题解
  • OpenClaw 飞书机器人权限配置与安全实践
  • Unity VR 高分辨率全景视频播放性能优化方案
  • Java 数据结构:树形结构与二叉树详解

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online