JAVA简单拼图游戏

本文介绍了如何通过一个基于Java的GUI游戏开发项目,学习和实践GUI应用程序开发、事件处理、数据结构和算法,以及计时器的应用,包括游戏窗口设计、拼图逻辑、用户交互和计时器的实现,展示了如何在实际项目中运用这些技术。
摘要由CSDN通过智能技术生成

通过这个游戏能学到什么?

GUI 应用程序开发

事件处理

数据结构和算法

计时器的应用

图形界面设计与美化

简单介绍

1. 游戏窗口和界面设计

首先,我们创建一个继承自JFrame的游戏窗口类PictureFrame。这个类负责显示游戏界面和处理游戏逻辑。在界面设计中,我们使用了JPanel来布置拼图块和按钮,同时使用了JLabel来显示游戏标题和背景图像。

2. 游戏数据和逻辑

游戏的拼图状态由一个二维数组dataS表示,其中0代表空格。我们定义了一个与之相对应的胜利状态数组winDatas,用于判断游戏是否胜利。游戏的核心逻辑包括移动拼图块、检查游戏状态是否胜利以及打乱拼图等功能。

3. 用户交互和事件处理

通过给按钮和窗口添加事件监听器来处理用户交互。用户可以通过按钮点击或键盘按键来移动拼图块。此外,还实现了一些其他功能,如显示提示信息、重置游戏等。

4. 计时器和界面更新

为了增加游戏的趣味性,我们添加了一个计时器来记录游戏时间。每秒钟计时器会更新一次,并在窗口标题栏显示当前游戏所用时间。同时,每次拼图块移动后,界面会重新绘制以反映最新的游戏状态。

关键代码

1.计时器

private Timer timer; // 计时器对象
private int seconds; // 计时器秒数

/**
 * 启动计时器,并更新计时器的秒数
 */
private void startTime() {
    // 创建计时器,每隔一秒触发一次 actionPerformed 方法
    timer = new Timer(1000, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            seconds++; // 每次触发计时器,秒数加一
            setTitle("动漫拼图 - 所用时间: " + seconds + "秒"); // 更新窗口标题,显示当前游戏进行的时间
        }
    });
    seconds = 0; // 初始化秒数为0
    timer.start(); // 启动计时器
}

首先声明了一个 Timer 对象 timer 和一个整型变量 seconds 来记录秒数。然后,定义了一个 startTime 方法用于启动计时器。在 startTime 方法中:

  1. 创建了一个 Timer 对象 timer,指定了计时器的触发间隔为1000毫秒(即1秒),并传入一个 ActionListener 匿名类作为参数。这个匿名类实现了 ActionListener 接口,重写了 actionPerformed 方法,在每次计时器触发时执行相应的操作。

  2. actionPerformed 方法中,每次计时器触发时,秒数 seconds 自增1,并调用 setTitle 方法更新窗口标题,显示当前游戏进行的时间。

  3. 最后,初始化秒数为0,并调用 timer.start() 启动计时器。
     

 2.二元数组

/**
 * 随机打乱拼图的顺序
 */
private void randomArry() {
    // 创建一个 Random 对象,用于生成随机数
    Random r = new Random();
    
    // 使用双重循环遍历二维数组
    for (int i = 0; i < dataS.length; i++) {
        for (int j = 0; j < dataS.length; j++) {
            // 生成两个随机数,分别表示要交换的另一个元素的行索引和列索引
            int x = r.nextInt(dataS.length);
            int y = r.nextInt(dataS[x].length);
            
            // 交换当前位置和随机位置的值
            int tmp = dataS[i][j];
            dataS[i][j] = dataS[x][y];
            dataS[x][y] = tmp;
        }
    }
    
    // 使用标签在循环中寻找空格(值为0)的位置
    wc:
    for (int i = 0; i < dataS.length; i++) {
        for (int j = 0; j < dataS.length; j++) {
            if (dataS[i][j] == 0) {
                x = i; // 记录空格的行索引
                y = j; // 记录空格的列索引
                break wc; // 跳出循环
            }
        }
    }
}
  1. 创建了一个 Random 对象 r,用于生成随机数。

  2. 使用两层嵌套的 for 循环遍历二维数组 dataS,在每个位置上进行数值交换。在内部循环中,首先生成两个随机数 xy,分别代表要交换的另一个元素的行索引和列索引。

  3. 交换数组中当前位置和随机位置的值。通过临时变量 tmp,将当前位置的值暂存起来,然后将当前位置的值更新为随机位置的值,最后将随机位置的值更新为暂存的值,完成交换操作。

  4. 使用标签 wc 在嵌套循环中实现了一个带标签的 break 语句,用于在找到空格(值为0)时跳出循环,以获取空格的位置

3. 随机数

/**
 * 随机打乱拼图的顺序
 */
private void randomArry() {
    // 创建一个 Random 对象,用于生成随机数
    Random r = new Random();
    
    // 使用双重循环遍历二维数组
    for (int i = 0; i < dataS.length; i++) {
        for (int j = 0; j < dataS.length; j++) {
            // 生成两个随机数,分别表示要交换的另一个元素的行索引和列索引
            int x = r.nextInt(dataS.length);
            int y = r.nextInt(dataS[x].length);
            
            // 交换当前位置和随机位置的值
            int tmp = dataS[i][j];
            dataS[i][j] = dataS[x][y];
            dataS[x][y] = tmp;
        }
    }
    
    // 使用标签在循环中寻找空格(值为0)的位置
    wc:
    for (int i = 0; i < dataS.length; i++) {
        for (int j = 0; j < dataS.length; j++) {
            if (dataS[i][j] == 0) {
                x = i; // 记录空格的行索引
                y = j; // 记录空格的列索引
                break wc; // 跳出循环
            }
        }
    }
}
  1. 创建 Random 对象 r:通过 Random 类的构造函数创建一个随机数生成器对象。
  2. 使用双重循环遍历二维数组 dataS:外层循环控制行,内层循环控制列,以访问二维数组的每个元素。
  3. 在内部循环中,使用 nextInt(int n) 方法生成两个随机数 xynextInt(int n) 方法返回一个在 [0, n) 范围内的随机整数。
  4. 使用生成的随机数 xy,交换当前位置 (i, j) 和随机位置 (x, y) 上的元素值:这样就实现了数组元素的随机打乱。
  5. 最后,在循环中寻找空格(值为0)的位置,并记录其行索引和列索引,以便后续游戏操作。

4.图片交换逻辑 

/**
 * 向上移动空格
 */
private void moveUp() {
    // 如果空格在最顶部,则不执行移动操作
    if (x == 0) {
        return;
    }
    // 如果空格不在最顶部,则执行向上移动操作
    if (x > 0) {
        // 交换空格和上方相邻方块的值
        dataS[x][y] = dataS[x - 1][y];
        dataS[x - 1][y] = 0;
        // 更新空格位置
        x--;
    }
    // 检查游戏是否成功
    if (isSuccess()) {
        success();
    }
    // 重新绘制游戏界面
    rePaintView();
}

/**
 * 向下移动空格
 */
private void moveDown() {
    // 如果空格在最底部,则不执行移动操作
    if (x == 3) {
        return;
    }
    // 如果空格不在最底部,则执行向下移动操作
    if (x < 3) {
        // 交换空格和下方相邻方块的值
        dataS[x][y] = dataS[x + 1][y];
        dataS[x + 1][y] = 0;
        // 更新空格位置
        x++;
    }
    // 检查游戏是否成功
    if (isSuccess()) {
        success();
    }
    // 重新绘制游戏界面
    rePaintView();
}

/**
 * 向左移动空格
 */
private void moveLeft() {
    // 如果空格在最左侧,则不执行移动操作
    if (y == 0) {
        return;
    }
    // 如果空格不在最左侧,则执行向左移动操作
    if (y > 0) {
        // 交换空格和左侧相邻方块的值
        dataS[x][y] = dataS[x][y - 1];
        dataS[x][y - 1] = 0;
        // 更新空格位置
        y--;
    }
    // 检查游戏是否成功
    if (isSuccess()) {
        success();
    }
    // 重新绘制游戏界面
    rePaintView();
}

/**
 * 向右移动空格
 */
private void moveRight() {
    // 如果空格在最右侧,则不执行移动操作
    if (y == 3) {
        return;
    }
    // 如果空格不在最右侧,则执行向右移动操作
    if (y < 3) {
        // 交换空格和右侧相邻方块的值
        dataS[x][y] = dataS[x][y + 1];
        dataS[x][y + 1] = 0;
        // 更新空格位置
        y++;
    }
    // 检查游戏是否成功
    if (isSuccess()) {
        success();
    }
    // 重新绘制游戏界面
    rePaintView();
}

 

  1. moveUp() 方法: 该方法用于将空格向上移动。首先检查空格是否在最顶部,如果不在顶部,则将空格与上方相邻方块的值进行交换,然后更新空格的位置。如果移动后游戏状态符合胜利条件,则调用 success() 方法将游戏状态设置为胜利状态。最后,调用 rePaintView() 方法重新绘制游戏界面。

  2. moveDown() 方法: 该方法用于将空格向下移动。首先检查空格是否在最底部,如果不在底部,则将空格与下方相邻方块的值进行交换,然后更新空格的位置。如果移动后游戏状态符合胜利条件,则调用 success() 方法将游戏状态设置为胜利状态。最后,调用 rePaintView() 方法重新绘制游戏界面。

  3. moveLeft() 方法: 该方法用于将空格向左移动。首先检查空格是否在最左侧,如果不在左侧,则将空格与左侧相邻方块的值进行交换,然后更新空格的位置。如果移动后游戏状态符合胜利条件,则调用 success() 方法将游戏状态设置为胜利状态。最后,调用 rePaintView() 方法重新绘制游戏界面。

  4. moveRight() 方法: 该方法用于将空格向右移动。首先检查空格是否在最右侧,如果不在右侧,则将空格与右侧相邻方块的值进行交换,然后更新空格的位置。如果移动后游戏状态符合胜利条件,则调用 success() 方法将游戏状态设置为胜利状态。最后,调用 rePaintView() 方法重新绘制游戏界面。

完整代码

 (1)主文件:PictureFrame

package com.game1;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyAdapter;
import java.util.Random;

/**
 * 游戏窗口类,用于显示游戏界面和处理游戏逻辑
 */
public class PictureFrame extends JFrame {
    // 游戏数据数组,用于存储拼图的状态
    private int[][] dataS = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    // 用于比较的胜利状态数组
    private int[][] winDatas = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    // 空格所在的行和列
    private int x;
    private int y;

    // 按钮和面板组件
    private JButton top;
    private JButton bottom;
    private JButton left;
    private JButton right;
    private JButton help;
    private JButton clear;
    private JPanel jp;

    // 计时器和秒数
    private Timer timer;
    private int seconds;

    // 时间标签
    private JLabel timeLabel;

    /**
     * 构造函数,初始化游戏界面和相关组件
     */
    public PictureFrame() {
        initFrame();
        randomArry();
        paintView();
        addButtonEvent();
        addKeyboardListener();

        // 启动计时器
        startTime();

        this.setVisible(true);
        this.setFocusable(true);
    }

    /**
     * 游戏成功时的操作,将游戏状态设置为胜利状态
     */
    private void success() {
        dataS = new int[][]{
                {1, 2, 3, 4},
                {5, 6, 7, 8},
                {9, 10, 11, 12},
                {13, 14, 15, 16}
        };
        // 禁用移动按钮
        top.setEnabled(false);
        right.setEnabled(false);
        bottom.setEnabled(false);
        left.setEnabled(false);
    }

    /**
     * 检查当前游戏状态是否为胜利状态
     *
     * @return 是否胜利
     */
    public boolean isSuccess() {
        for (int i = 0; i < dataS.length; i++) {
            for (int j = 0; j < dataS[i].length; j++) {
                if (dataS[i][j] != winDatas[i][j]) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 重新绘制游戏界面
     */
    private void rePaintView() {
        jp.removeAll();
        for (int i = 0; i < dataS.length; i++) {
            for (int j = 0; j < dataS[i].length; j++) {
                JLabel imageLabel = new JLabel(new ImageIcon("day09\\images\\" + dataS[i][j] + ".png"));
                imageLabel.setBounds(j * 90, i * 90, 90, 90);
                jp.add(imageLabel);
                jp.repaint();
            }
        }
    }

    /**
     * 给按钮添加事件监听器
     */
    private void addButtonEvent() {
        top.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                moveUp();
                requestFocusInWindow();
            }
        });
        bottom.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                moveDown();
                requestFocusInWindow();
            }
        });
        left.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                moveLeft();
                requestFocusInWindow();
            }
        });
        right.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                moveRight();
                requestFocusInWindow();
            }
        });
        help.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(PictureFrame.this, "联系:Dlop", "提示", JOptionPane.INFORMATION_MESSAGE);
            }
        });
        clear.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                randomArry();
                rePaintView();
            }
        });
    }

    /**
     * 添加键盘事件监听器
     */
    private void addKeyboardListener() {
        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                int keyCode = e.getKeyCode();
                switch (keyCode) {
                    case KeyEvent.VK_UP:
                        moveUp();
                        break;
                    case KeyEvent.VK_DOWN:
                        moveDown();
                        break;
                    case KeyEvent.VK_LEFT:
                        moveLeft();
                        break;
                    case KeyEvent.VK_RIGHT:
                        moveRight();
                        break;
                }
            }
        });
    }

    /**
     * 向上移动空格
     */
    private void moveUp() {
        if (x == 0) {
            return;
        }
        if (x > 0) {
            dataS[x][y] = dataS[x - 1][y];
            dataS[x - 1][y] = 0;
            x--;
        }
        if (isSuccess()) {
            success();
        }
        rePaintView();
    }

    /**
     * 向下移动空格
     */
    private void moveDown() {
        if (x == 3) {
            return;
        }
        if (x < 3) {
            dataS[x][y] = dataS[x + 1][y];
            dataS[x + 1][y] = 0;
            x++;
        }
        if (isSuccess()) {
            success();
        }
        rePaintView();
    }

    /**
     * 向左移动空格
     */
    private void moveLeft() {
        if (y == 0) {
            return;
        }
        if (y > 0) {
            dataS[x][y] = dataS[x][y - 1];
            dataS[x][y - 1] = 0;
            y--;
        }
        if (isSuccess()) {
            success();
        }
        rePaintView();
    }

    /**
     * 向右移动空格
     */
    private void moveRight() {
        if (y == 3) {
            return;
        }
        if (y < 3) {
            dataS[x][y] = dataS[x][y + 1];
            dataS[x][y + 1] = 0;
            y++;
        }
        if (isSuccess()) {
            success();
        }
        rePaintView();
    }

    /**
     * 随机打乱拼图
     */
    private void randomArry() {
        Random r = new Random();
        for (int i = 0; i < dataS.length; i++) {
            for (int j = 0; j < dataS.length; j++) {
                int x = r.nextInt(dataS.length);
                int y = r.nextInt(dataS[x].length);
                int tmp = dataS[i][j];
                dataS[i][j] = dataS[x][y];
                dataS[x][y] = tmp;
            }
        }
        wc:
        for (int i = 0; i < dataS.length; i++) {
            for (int j = 0; j < dataS.length; j++) {
                if (dataS[i][j] == 0) {
                    x = i;
                    y = j;
                    break wc;
                }
            }
        }
    }

    /**
     * 绘制游戏界面
     */
    private void paintView() {
        JLabel title = new JLabel(new ImageIcon("day09\\images\\title.png"));
        title.setBounds(354, 27, 232, 57);
        this.add(title);

        jp = new JPanel();
        jp.setBounds(150, 114, 360, 360);
        jp.setLayout(null);
        for (int i = 0; i < dataS.length; i++) {
            for (int j = 0; j < dataS[i].length; j++) {
                JLabel imageLabel = new JLabel(new ImageIcon("day09\\images\\" + dataS[i][j] + ".png"));
                imageLabel.setBounds(90 * j, 90 * i, 90, 90);
                jp.add(imageLabel);
            }
        }
        this.add(jp);

        JLabel canZhao = new JLabel(new ImageIcon("day09\\images\\canzhaotu.png"));
        canZhao.setBounds(574, 114, 122, 121);
        this.add(canZhao);

        top = new JButton(new ImageIcon("day09\\images\\shang.png"));
        bottom = new JButton(new ImageIcon("day09\\images\\xia.png"));
        left = new JButton(new ImageIcon("day09\\images\\zuo.png"));
        right = new JButton(new ImageIcon("day09\\images\\you.png"));
        help = new JButton(new ImageIcon("day09\\images\\qiuzhu.png"));
        clear = new JButton(new ImageIcon("day09\\images\\chongzhi.png"));

        top.setBounds(732, 265, 57, 57);
        bottom.setBounds(732, 347, 57, 57);
        left.setBounds(650, 347, 57, 57);
        right.setBounds(813, 347, 57, 57);
        help.setBounds(626, 444, 108, 45);
        clear.setBounds(786, 444, 108, 45);

        this.add(top);
        this.add(bottom);
        this.add(left);
        this.add(right);
        this.add(help);
        this.add(clear);

        JLabel bgcImage = new JLabel(new ImageIcon("day09\\images\\background.png"));
        bgcImage.setBounds(0, 0, 960, 530);
        this.add(bgcImage);
    }

    /**
     * 初始化窗口
     */
    private void initFrame() {
        this.setSize(960, 565);
        this.setTitle("动漫拼图");
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(null);

    }

    /**
     * 启动计时器,并更新计时器的秒数
     */
    private void startTime() {
        timer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                seconds++;
                setTitle("动漫拼图 - 所用时间: " + seconds + "秒");
            }
        });
        seconds = 0;
        timer.start();
    }
}

(2)程序入口:App

package com.game1;

public class App {
    public static void main(String[] args) {
        PictureFrame jf = new PictureFrame();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值