java基础教程之拼图游戏的实现

Java 基础教程之拼图游戏的实现

1. 游戏介绍

拼图是一种经典的益智游戏,目的是将图片划分成若干个小块并打乱排列,然后将其重新组合成完整的图片。在这个项目中,我们将使用 Java 语言实现一个简单的拼图游戏,涉及的主要知识点包括 Java Swing 及基本的面向对象编程。

2. 实现步骤

2.1 项目初始化

首先,我们需要创建一个 Java 项目,并添加必要的依赖项,例如 Java Swing 库、Lombok 库等。此外,我们还需要准备一些图片资源作为游戏的素材。

2.2 实现游戏主界面

接下来,我们需要设计并实现游戏的主界面,包括图片显示区域、计时器、步数计数器等。这部分内容涉及 Java Swing 的布局、事件监听等基础知识。

public class PuzzleFrame extends JFrame {
    private static final int ROWS = 3; // 拼图行数
    private static final int COLS = 3; // 拼图列数
    private static final int IMAGE_SIZE = 180; // 图片大小
    private static final int GAP_SIZE = 5; // 图片间距

    private final JPanel boardPanel; // 拼图面板
    private final JLabel timeLabel; // 计时器标签
    private final JLabel stepsLabel; // 步数计数器标签

    public PuzzleFrame(){
        // 初始化窗口大小和标题
        setSize((IMAGE_SIZE + GAP_SIZE) * COLS, (IMAGE_SIZE + GAP_SIZE) * ROWS + 50);
        setTitle("Puzzle Game");

        // 创建面板并设置布局
        JPanel mainPanel = new JPanel();
        boardPanel = new JPanel();
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
        boardPanel.setLayout(new GridLayout(ROWS, COLS, GAP_SIZE, GAP_SIZE));

        // 添加拼图块
        List<ImageIcon> icons = loadImages();
        List<JButton> cells = createCells(icons);
        for(JButton cell : cells){
            boardPanel.add(cell);
        }

        // 创建控制面板并设置布局
        JPanel controlPanel = new JPanel();
        controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS));

        // 添加计时器
        timeLabel = new JLabel("Time: 0s");
        controlPanel.add(timeLabel);
        controlPanel.add(Box.createHorizontalStrut(20));

        // 添加步数计数器
        stepsLabel = new JLabel("Steps: 0");
        controlPanel.add(stepsLabel);
        controlPanel.add(Box.createHorizontalStrut(20));

        // 添加“重新开始”按钮
        JButton restartButton = new JButton("Restart");
        restartButton.addActionListener(e -> {
            restartGame();
        });
        controlPanel.add(restartButton);

        // 将面板添加到窗口中
        mainPanel.add(boardPanel);
        mainPanel.add(Box.createVerticalStrut(5));
        mainPanel.add(controlPanel);
        add(mainPanel);

        // 设置窗口可见性和关闭方式
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    /**
     * 读取图片资源
     */
    private List<ImageIcon> loadImages(){
        List<ImageIcon> icons = new ArrayList<>();
        for(int i = 1; i <= ROWS * COLS; i++){
            ImageIcon icon = new ImageIcon("img/" + i + ".jpg");
            Image img = icon.getImage().getScaledInstance(IMAGE_SIZE, IMAGE_SIZE, Image.SCALE_SMOOTH);
            icons.add(new ImageIcon(img));
        }
        return icons;
    }

    /**
     * 创建拼图块
     */
    private List<JButton> createCells(List<ImageIcon> icons){
        List<JButton> cells = new ArrayList<>();
        for(ImageIcon icon : icons){
            JButton cell = new JButton(icon);
            cell.setBorder(BorderFactory.createEmptyBorder());
            cell.addActionListener(e -> {
                cellClicked(cell);
            });
            cells.add(cell);
        }
        Collections.shuffle(cells);
        return cells;
    }

    /**
     * 点击拼图块触发的事件
     */
    private void cellClicked(JButton cell){
        // TODO: 实现拼图块移动逻辑
    }

    /**
     * 重新开始游戏
     */
    private void restartGame(){
        // TODO: 实现重置拼图逻辑
    }
}

以上代码实现了游戏主界面的布局及拼图块的加载,但还未实现拼图块的移动及拼图完成的判断。

2.3 实现拼图块移动逻辑

拼图块的移动涉及到交换两个块的位置,因此我们需要在代码中保存拼图块的位置信息及其对应的元素。具体实现可以使用一个二维数组来表示拼图面板,其中元素值为相应的拼图块对象。在每次点击时,判断目标位置是否为合法位置并进行移动操作。

public class PuzzleFrame extends JFrame {
    // ...

    private JButton[][] cells = new JButton[ROWS][COLS]; // 拼图块数组
    private int emptyRow = ROWS - 1; // 空白块所在行
    private int emptyCol = COLS - 1; // 空白块所在列
    private int steps = 0; // 步数
    private boolean gameOver = false; // 游戏是否结束

    // ...

    /**
     * 创建拼图块
     */
    private void createCells(List<ImageIcon> icons){
        for(int i = 0; i < ROWS; i++){
            for(int j = 0; j < COLS; j++){
                JButton cell = new JButton();
                cell.setBorder(BorderFactory.createEmptyBorder());
                if(i == emptyRow && j == emptyCol){
                    // 如果当前是空白块,则不设置图标
                } else {
                    ImageIcon icon = icons.get(i * COLS + j);
                    Image img = icon.getImage().getScaledInstance(IMAGE_SIZE, IMAGE_SIZE, Image.SCALE_SMOOTH);
                    cell.setIcon(new ImageIcon(img));
                }
                cell.addActionListener(e -> {
                    cellClicked(cell);
                });
                cells[i][j] = cell; // 加入二维数组
                boardPanel.add(cell); // 加入面板
            }
        }
    }

    /**
     * 点击拼图块触发的事件
     */
    private void cellClicked(JButton cell){
        if(gameOver){
            return;
        }
        int row = -1, col = -1;
        // 遍历二维数组查找所在位置
        for(int i = 0; i < ROWS; i++){
            for(int j = 0; j < COLS; j++){
                if(cells[i][j] == cell){
                    row = i;
                    col = j;
                    break;
                }
            }
        }
        if(isLegalMove(row, col)){
            // 保存移动前的坐标
            int prevRow = emptyRow;
            int prevCol = emptyCol;
            // 移动拼图块
            cells[emptyRow][emptyCol].setIcon(cell.getIcon());
            cells[row][col].setIcon(null);
            emptyRow = row;
            emptyCol = col;
            // 更新步数
            steps++;
            stepsLabel.setText("Steps: " + steps);
            // 判断是否完成拼图
            if(isGameComplete()){
                gameOver = true;
                JOptionPane.showMessageDialog(null, "Congratulations! You win in " + gameDuration() + "s and " + steps + " steps!");
            }
        }
    }

    /**
     * 判断目标位置是否为合法位置
     */
    private boolean isLegalMove(int row, int col){
        return ((row == emptyRow && (col == emptyCol - 1 || col == emptyCol + 1)) ||
                (col == emptyCol && (row == emptyRow - 1 || row == emptyRow + 1)));
    }

    /**
     * 判断拼图是否完成
     */
    private boolean isGameComplete(){
        int count = 1;
        for(int i = 0; i < ROWS; i++){
            for(int j = 0; j < COLS; j++){
                if(cells[i][j] == null || (i == emptyRow && j == emptyCol)){
                    continue;
                } else {
                    ImageIcon icon = (ImageIcon) cells[i][j].getIcon();
                    if(Integer.parseInt(icon.getDescription()) != count){
                        return false;
                    }
                    count++;
                }
            }
        }
        return true;
    }
}

以上代码实现了拼图块的移动及完成拼图后的提示等功能。

2.4 实现拼图复位逻辑

为了支持游戏重玩,我们需要对拼图块进行复位操作,这可以通过打乱拼图块数组的顺序来实现。此外,我们还需要重置步数计数器及计时器等变量。

public class PuzzleFrame extends JFrame {
    // ...

    /**
     * 重置拼图
     */
    private void restartGame(){
        Collections.shuffle(Arrays.asList(cells).stream().flatMap(Arrays::stream).collect(Collectors.toList()));
        emptyRow = ROWS - 1;
        emptyCol = COLS - 1;
        steps = 0;
        stepsLabel.setText("Steps: 0");
        gameOver = false;
    }

    /**
     * 计算游戏时长
     */
    private long gameDuration(){
        // TODO: 计算游戏时长
    }
}

2.5 完成游戏时长计算功能

最后,我们需要实现游戏时长计算功能。这可以通过侦听器的方式来实现,我们可以使用 Timer 类来辅助计时器的实现。

public class PuzzleFrame extends JFrame {
    // ...

    private Timer gameTimer = new Timer(1000, e -> {
        int elapsed = Integer.parseInt(timeLabel.getText().substring(6, timeLabel.getText().length() - 1));
        timeLabel.setText("Time: " + (elapsed + 1) + "s");
    }); // 计时器

    /**
     * 开始新游戏
     */
    public void startGame(){
        restartGame();
        gameTimer.start();
    }

    /**
     * 计算游戏时长
     */
    private long gameDuration(){
        int elapsed = Integer.parseInt(timeLabel.getText().substring(6, timeLabel.getText().length() - 1));
        gameTimer.stop();
        return elapsed;
    }
}

示例

示例 1: 开始新游戏
public static void main(String[] args){
    PuzzleFrame puzzle = new PuzzleFrame();
    puzzle.startGame();
}
示例 2: 拼图块移动

这里假设我们已经点击了拼图块 cells[1][2]

// 执行移动操作
cells[1][1].setIcon(cells[1][2].getIcon());
cells[1][2].setIcon(null);
emptyRow = 1;
emptyCol = 2;
// 更新步数
steps++;
stepsLabel.setText("Steps: " + steps);
// 判断是否完成拼图
if(isGameComplete()){
    gameOver = true;
    JOptionPane.showMessageDialog(null, "Congratulations! You win in " + gameDuration() + "s and " + steps + " steps!");
}

结语

上述代码实现了一个简单的拼图游戏,其依据代码框架可以进行更多功能的扩展和优化。此外,Java Swing 还有许多其他强大的功能可以用于构建更复杂的用户界面,希望读者可以进一步探索。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java基础教程之拼图游戏的实现 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Android性能优化之捕获java crash示例解析

    关于“Android性能优化之捕获java crash示例解析”的完整攻略,我会从以下方面进行详细讲解: 什么是Java Crash? Java Crash是指在Android应用程序中发生了Java异常并导致应用程序崩溃的情况。Java异常是指程序执行过程中出现错误而无法进行正常处理的情况。在应用中,可能会出现各种类型的Java异常,如NullPointe…

    Java 2023年5月27日
    00
  • 在Java的Spring框架的程序中使用JDBC API操作数据库

    使用JDBC API操作数据库是Java程序开发的基本技能之一,而在Spring框架中使用JDBC则是非常常见的情况。下面是在Java的Spring框架中使用JDBC API操作数据库的完整攻略。 配置JDBC数据源 在Spring中,我们需要先配置一个JDBC数据源。数据源的配置通常是在Spring的XML配置文件中完成的。如下是一个典型的JDBC数据源配…

    Java 2023年5月20日
    00
  • java实现文件打包压缩输出到浏览器下载

    下面是Java实现文件打包压缩输出到浏览器下载的详细攻略。 一、引入相关依赖 我们需要使用Java自带的ZipOutputStream类和ServletOutputStream类来实现文件压缩和下载功能。 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; impo…

    Java 2023年5月26日
    00
  • java使用jdbc操作数据库示例分享

    下面是关于“java使用jdbc操作数据库示例分享”的完整攻略: 1. 准备工作 首先,我们需要准备好以下工具和环境:- JDK 1.8 或以上版本- MySQL 数据库- MySQL JDBC 驱动程序- IDE 工具(如 IntelliJ IDEA)或者代码编写器(如 VS Code) 2. 下载并导入JDBC驱动 要使用 JDBC 操作数据库,需要下载…

    Java 2023年6月16日
    00
  • 详解Java程序读取properties配置文件的方法

    当我们需要在Java程序中读取一些配置信息时,通常可以使用Properties文件作为配置文件,这种方式比硬编码更加灵活、易于维护。 以下是读取Properties配置文件的方法: 准备Properties文件 首先需要准备一个Properties文件。文件扩展名通常为.properties,它是一个文本文件,可以使用任何文本编辑器来编辑。属性文件由键值对组…

    Java 2023年5月20日
    00
  • java获取日期的方法

    当我们编写Java程序时,常常需要获取当前的日期和时间,以及进行时间的加减计算和格式化输出。下面是获取日期的方法的攻略。 获取当前日期 要获取当前日期,可以使用Java自带的日期类java.util.Date,这个类表示一个精确到毫秒的时间点,我们可以通过它获取当前日期并对其进行格式化。 import java.util.Date; import java.…

    Java 2023年5月20日
    00
  • Java 按照字节来截取字符串的代码(不会出现半个汉字)

    下面是Java按照字节来截取字符串的代码攻略: 1. 背景介绍 在Java中,字符串常常需要截取一部分进行处理,而其中有一种情况是按照字节来截取字符串。这主要是因为在多字节字符集中,一个汉字可能由2个以上的字节表示,如果对一个汉字进行简单的截取,可能会导致截取到半个汉字,出现乱码等问题。因此,我们需要了解如何按照字节来截取字符串。 2. 方案分析 实现按照字…

    Java 2023年5月27日
    00
  • 浅谈Java基准性能测试之JMH

    浅谈Java基准性能测试之JMH 什么是基准性能测试? 基准性能测试是一种通过对软件或硬件系统进行压力测试来衡量其性能水平的方法。通常,在执行基准性能测试之前,我们需要明确目标,比如检查系统的吞吐量、响应时间和负载下的资源消耗等。 为什么要进行基准性能测试? 在软件开发过程中,我们需要不断地优化代码,以期提高系统的性能和可靠性。而基准性能测试为我们提供了一种…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部