> 技术文档 > Java 拼图小游戏开发全记录:从 0 到 1 实现经典益智项目

Java 拼图小游戏开发全记录:从 0 到 1 实现经典益智项目


个人主页-爱因斯晨

文章专栏-JAVA学习

最近学习人工智能时遇到一个好用的网站分享给大家:
人工智能学习

文章目录

    • 个人主页-爱因斯晨
    • 文章专栏-JAVA学习
    • 一、项目设计与准备工作
      • 1.1 功能定位
      • 1.2 开发环境
      • 1.3 项目结构
    • 二、基础界面搭建(Step 1)
    • 三、图片加载与分割(Step 2)
    • 四、核心逻辑实现(Step 3)
      • 4.1 打乱算法
      • 4.2 鼠标交互
    • 五、功能完善与优化(Step 4)
      • 5.1 计时与步数统计
      • 5.2 界面美化
    • 六、项目总结与拓展方向
      • 6.1 开发收获
      • 6.2 拓展建议
      • 6.3 完整代码结构
    • 六、项目总结与拓展方向
      • 6.1 开发收获
      • 6.2 拓展建议
      • 6.3 完整代码结构

作为 Java 初学者,实战项目是巩固知识的最佳方式。本文将带大家从零开始开发一款拼图小游戏,涵盖界面设计、核心逻辑与交互优化,全程配套可运行代码,适合零基础学习者上手实践。
Java 拼图小游戏开发全记录:从 0 到 1 实现经典益智项目

一、项目设计与准备工作

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.javaimport 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; // 每个方块大小 private int[][] data = new int[SIZE][SIZE]; // 存储方块编号 public PuzzleFrame() { initFrame(); initMenu(); initData(); setVisible(true); } // 初始化窗口属性 private void initFrame() { setTitle(\"Java拼图游戏\"); setSize(SIZE * BLOCK_SIZE + 50, SIZE * BLOCK_SIZE + 100); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); // 居中显示 setLayout(null); // 绝对布局,方便控制方块位置 } // 初始化菜单 private void initMenu() { JMenuBar menuBar = new JMenuBar(); JMenu gameMenu = new JMenu(\"游戏\"); JMenuItem restartItem = new JMenuItem(\"重新开始\"); JMenuItem exitItem = new JMenuItem(\"退出\"); gameMenu.add(restartItem); gameMenu.add(exitItem); menuBar.add(gameMenu); setJMenuBar(menuBar); // 退出功能 exitItem.addActionListener(e -> System.exit(0)); } // 初始化数据(1-8为方块,0为空位) private void initData() { for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { data[i][j] = i * SIZE + j + 1; } } data[SIZE-1][SIZE-1] = 0; // 右下角为空位 } public static void main(String[] args) { new PuzzleFrame(); }}

关键知识点

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

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

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

// ImageUtil.javaimport 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方法绘制界面@Overridepublic 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 完整代码结构

和背景:

// 重写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 基础知识,更能理解小型应用的开发流程。