拼图小游戏
采用主技术
GUI Java多线程 IO流
效果展示:
启动界面:

主界面:(游戏界面内容功能见名知意)
阅前须知
源码分为两部分
1、图片image
2、代码实现
在导入之前需要修改IO文件路径
如需修改拼图照片内容,需要将主图像素大小调至450 x 600
然后拆分为6行5列作为拼图区
注意图片命名格式一定要一致
附上一个拆分图片网站:https://www.qtool.net/piccutting
创作不易,如果能帮到您,请点赞支持,感谢!
源码部分
ResourceUtil
package jinhuan.pink;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class ResourceUtil {
public static BufferedImage getImage(String imageName){
BufferedImage bufferedImage = null;
try {
// 这里的路径需要修改为自己的文件路径
bufferedImage = ImageIO.read(new File("Xingle\\src\\image\\"+imageName));
} catch (IOException e) {
e.printStackTrace();
}
return bufferedImage;
}
}
MyTime
package jinhuan.pink;
/**
* 自定义计时器
* 实现时间的递增
* */
public class MyTime implements Runnable {
static String myTime;
static int myMinute = 0;
static int mySecond = 0;
static int myHour = 0;
@Override
public void run() {
while (true){
try {
Thread.currentThread().sleep(1000);
mySecond++;
if(mySecond >= 60){
mySecond = 0;
myMinute++;
}
if(myMinute >= 60){
myMinute++;
}
if(myHour >= 24){
myMinute++;
}
myTime = myHour+":"+myMinute+":"+mySecond;
MainJFrame.buttonAreaJPanel.yourtime.setText("时间:"+myTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
GameStart
package jinhuan.pink;
//主启动类
public class GameStart {
public static void main(String[] args) {
new BeginJFrame();
}
}
BeginJFrame
package jinhuan.pink;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
/**
* 游戏的开始界面
* */
public class BeginJFrame extends JFrame {
private BeginJFrame beginJFrame = this;
private BufferedImage titleIcon = ResourceUtil.getImage("icon.png");
private BufferedImage startBu = ResourceUtil.getImage("startBu.png");
private JButton start = new JButton(new ImageIcon(startBu));
private Container contentPane = getContentPane();
/**
* 定义主窗口的组件
*/
private JLabel title = new JLabel("拼图小游戏",JLabel.CENTER );
private JLabel hhh = new JLabel("开始游戏",JLabel.CENTER );
/**
* 定义主窗体的子面板
* */
private JPanel mainJpanel = new JPanel(null);
// 获取本窗体的自带面板并将其属化
public BeginJFrame(){
addComponent();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
setTitle("拼图小游戏");
setSize(600,300);
setIconImage(titleIcon);
setLocationRelativeTo(null);
setVisible(true);
}
public void addComponent(){
contentPane.setBackground(Color.getHSBColor(12,12,12));
title.setFont(new Font("宋体",Font.BOLD,60));
contentPane.add(title,BorderLayout.NORTH);
/**
* 给两个按钮设置属性并添加监听事件
* */
start.setBounds(200,100,200,76);
hhh.setFont(new Font("幼圆",Font.BOLD,20));
start.setFocusPainted(false);
start.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new MainJFrame();
beginJFrame.dispose();
}
});
hhh.setBounds(200,50,200,76);
mainJpanel.add(hhh);
mainJpanel.add(start);
mainJpanel.setBackground(Color.WHITE);
contentPane.add(mainJpanel,BorderLayout.CENTER);
}
}
MainJFrame
package jinhuan.pink;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class MainJFrame extends JFrame {
/**
* 创建整个面板的属性变量
* 窗体的图标:
* iconImage-->创建工具类,利用IO进行批量的读入
* 两个子面板:
* buttonAreaJPanel-->代表按钮面板
* imageAreaJPanel-->代表下部图片区域
*
* */
BufferedImage iconImage = ResourceUtil.getImage("icon.png");// 代表整个游戏的图标
static ButtonAreaJPanel buttonAreaJPanel = new ButtonAreaJPanel();//按钮区域对象
static ImageAreaJPanel imageAreaJPanel = new ImageAreaJPanel();// 创建图片区域对象
/**
* 提供无参的构造方法:
* 添加事件监听
* 设置窗口属性(可见设置在最后)
* */
public MainJFrame(){
//调用监听的方法
this.setIconImage(iconImage);
this.setTitle("拼图小游戏");
this.setSize(1200, 720);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);// 设置窗体居中
this.setResizable(false);// 设置窗体的大小不可以改变
this.add(buttonAreaJPanel, BorderLayout.NORTH);
this.add(imageAreaJPanel, BorderLayout.CENTER);
this.setVisible(true);
}
}
ButtonAreaJPanel
package jinhuan.pink;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class ButtonAreaJPanel extends JPanel{
/**
* 为当前面板添加两个子面板:
* 1、左面板--leftJPanel
* 2、右面板--rightJPanel
* */
JPanel leftJPanel = new JPanel();
JPanel rightJPanel = new JPanel();
/**
* 构造方法来初始化面板
* 1、设置此面板的属性
* 2、将左边的面板和右边的面板添加到按钮区域面板中
* */
public ButtonAreaJPanel() {
this.setLayout(new GridLayout(1, 2));
addLeftJPanel();
addRightJPanel();
}
/**
* 1、设置左边面板的属性
* 2、定义左边面板的组件:
* a、imageName代表左边的图片名称
* b、step代表计步区域
* */
static JTextField imageName;
static JTextField step;
static JTextField yourtime;
public void addLeftJPanel() {
leftJPanel.setBorder(new TitledBorder("游戏数据区"));
leftJPanel.setLayout(new GridLayout(1,2));
/**
* 图片显示的文本框
* */
imageName = new JTextField("图片名称:"+pictures[0]);
imageName.setBackground(Color.CYAN);
imageName.setEditable(false);
/**
* 步数的文本显示框
* */
step = new JTextField("步数:"+0);//
step.setBackground(Color.magenta);
step.setEditable(false);
/**
* yourtime
* */
yourtime = new JTextField("花费时间:"+0);//
yourtime.setBackground(Color.PINK);
yourtime.setEditable(false);
// 添加至左面板
leftJPanel.add(imageName,BorderLayout.WEST);
leftJPanel.add(yourtime,BorderLayout.CENTER);
leftJPanel.add(step, BorderLayout.EAST);
// 将leftJPanel添加到ButtonAreaJPanel
this.add(leftJPanel, BorderLayout.WEST);
}
/**
* 定义右边面板的组件:
* 1、单选按钮:——>分组
* a、showNumber代表显示序号
* b、hideNumber代表隐藏序号-->默认被选中
* 2、下拉列表——>来选择图片
* 3、开始按钮
* */
static String[] pictures = {"波妞和宗介_1","波妞和宗介_2","宗介"};//作为下拉列表的选项
static String[] models = {"简单","中等","困难"};
static JRadioButton showNum;
static JRadioButton hideNum;
static JComboBox<String> changePic;
static JComboBox<String> changeMod;
static JButton start;
static int modIndex;
static int indexBegin;
static long beginTime;
MyTime myTime = new MyTime();
Thread thread = new Thread(myTime);
public void addRightJPanel() {
rightJPanel.setBorder(new TitledBorder("功能区"));//设置 面板的边框的标题
/**
* 完成 两个单选框的初始化
* */
showNum = new JRadioButton("显示序号",false);
showNum.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
ImageAreaJPanel.myPuzzle.showNum();
}
});
hideNum = new JRadioButton("隐藏序号",true);
hideNum.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ImageAreaJPanel.myPuzzle.hideNum();
}
});
// 将两个按钮绑定为一组
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(showNum);
buttonGroup.add(hideNum);
// 添加到右边的面板上
rightJPanel.add(showNum);
rightJPanel.add(hideNum);
// 对下拉列表进行设置
changePic = new JComboBox<String>(pictures);//将图片名称的数组进行传入
changeMod = new JComboBox<String>(models);//设置难度
changePic.addItemListener(new ItemListener(){
@Override
public void itemStateChanged(ItemEvent e) {
if(e.getStateChange() == ItemEvent.SELECTED) {
hideNum.setSelected(false);// 切换图片默认不显示序号
int index = changePic.getSelectedIndex();//获取选中的图片的索引 index 第一个选项 0
PreviewAreaJPanel.picId = index + 1;// 通过获取的索引 确定图片的id ----> 修改图片的id
ImageAreaJPanel.myPreview.repaint(); //重新绘制
imageName.setText("图片名称:"+changePic.getSelectedItem());//设置功能区显示的图片名称
PreviewAreaJPanel.stepCount = 0;//当游戏切换图片之后,步数清空,重新赋值为0
//将总共用了多少步数 赋值给文本框
step.setText("当前步数:"+PreviewAreaJPanel.stepCount);
MainJFrame.buttonAreaJPanel.yourtime.setText("时间:"+MyTime.myTime);
// thread.interrupt();
ImageAreaJPanel.myPuzzle.reload();
}
}
});
changeMod.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
modIndex = changeMod.getSelectedIndex();
}
});
changePic.setBackground(Color.WHITE);
changeMod.setBackground(Color.WHITE);
//将下拉列表添加到右边的面板上
rightJPanel.add(new JLabel("选择图片:"));
rightJPanel.add(changePic);
rightJPanel.add(new JLabel("选择难度:"));
rightJPanel.add(changeMod);
/**
* 完成开始按钮的初始化
* 并给开始按钮添加监听事件
* */
start = new JButton("开始游戏");
start.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
beginTime = System.currentTimeMillis();
PreviewAreaJPanel.stepCount = 0;
PreviewAreaJPanel.wasteTime = "";
MainJFrame.buttonAreaJPanel.step.setText("步数:"+PreviewAreaJPanel.stepCount);
myTime.mySecond = 0;
myTime.myMinute = 0;
myTime.myHour = 0;
if(indexBegin == 0){
thread.start();
}
indexBegin++;
ImageAreaJPanel.myPuzzle.upset();
}
});
start.setFocusPainted(false);//去除获取焦点时的边框线
// 将开始按钮添加到右边的面板中
rightJPanel.add(start);
// 将整个右边的面板添加到按钮区
this.add(rightJPanel, BorderLayout.EAST);
}
}
ImageAreaJPanel
package jinhuan.pink;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;
public class ImageAreaJPanel extends JPanel{
/**
* 下面板的两个子面板
* 一个是原图区
* 一个是拼图区
* */
static PreviewAreaJPanel myPreview = new PreviewAreaJPanel();
static PuzzleAreaJPanel myPuzzle = new PuzzleAreaJPanel();
/**
* 构造方法完成图片区域的初始化
* */
public ImageAreaJPanel() {
this.setLayout(new GridLayout(1, 2));
myPreview.setBorder(new TitledBorder("图片预览区"));
myPuzzle.setBorder(new TitledBorder("拼图区"));
this.add(myPreview, BorderLayout.WEST);
this.add(myPuzzle, BorderLayout.EAST);
}
}
PreviewAreaJPanel
package jinhuan.pink;
//图片预览区
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class PreviewAreaJPanel extends JPanel{
//定义两个变量
//图片的数字 ----> static 修饰的成员 可以直接通过类名.访问 静态变量:类变量 随着类的加载而 初始化的 存在于 方法区的静态区
static int picId = 1;
//总共用了多少步
static int stepCount = 0;
static String wasteTime = "";
//预览区的图片
BufferedImage previewImage ;
//绘制图片
@Override
public void paint(Graphics g) {
super.paint(g);
previewImage = ResourceUtil.getImage(picId + ".jpg");
g.drawImage(previewImage, 70, 20, 450, 600, null);
}
}
PuzzleAreaJPanel
package jinhuan.pink;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
public class PuzzleAreaJPanel extends JPanel implements MouseListener {
static class PictureButton extends JButton {
// 构造方法完成按钮的初始化
public PictureButton(Icon icon) {
super(icon);
this.setSize(90, 100);
this.setHorizontalTextPosition(CENTER);
this.setVerticalTextPosition(CENTER);
}
/**
* 定义小方格按钮的运动的方法
* */
public void movePictureButton(String dir) {
switch (dir) {
case "RIGHT":
this.setLocation(this.getBounds().x + 90, this.getBounds().y);
break;
case "LEFT":
this.setLocation(this.getBounds().x - 90, this.getBounds().y);
break;
case "UP":
this.setLocation(this.getBounds().x, this.getBounds().y - 100);
break;
case "DOWN":
this.setLocation(this.getBounds().x , this.getBounds().y + 100);
break;
}
}
}
/**
* 设置拼图区域
* 1、设置29个按钮
* a、图片来添加到按钮上
* b、for循环创建按钮(记得减去一个按钮)
* 2、一个为空白按钮
* */
static PictureButton[] picButtons;
static Rectangle rectangle;
static BufferedImage showImage;
/**
* 构造方法完成属性的初始化
* */
public PuzzleAreaJPanel() {
picButtons = new PictureButton[6 * 5];
this.setLayout(null);
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 5; j++) {
showImage = ResourceUtil.getImage(PreviewAreaJPanel.picId + "_" + (5 * i + j + 1) + ".jpg");
picButtons[5 * i + j] = new PictureButton(new ImageIcon(showImage));
picButtons[5 * i + j].setLocation(70 + j * 90, 20 + i * 100);
this.add(picButtons[5 * i + j]);
}
}
// 删除最后一个小方格
this.remove(picButtons[picButtons.length - 1]);
// 设置最后的空白格的坐标来将其初始化
rectangle = new Rectangle(70 + 360, 20 + 500, 90, 100);
}
/**
* 显示序号的方法
* */
public void showNum() {
// 循环为每个按钮添加文本
for (int i = 0; i < picButtons.length - 1; i++) {
picButtons[i].setText("" + (i + 1));
}
}
/**
* 隐藏序号的方法
* */
public void hideNum() {
// 循环为每个按钮添加文本
for (int i = 0; i < picButtons.length - 1; i++) {
picButtons[i].setText("");
}
}
/**
* 完成拼图区的图片重新加载
* 获取当前picId 重新设置
* */
public void reload() {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 5; j++) {
showImage = ResourceUtil.getImage(PreviewAreaJPanel.picId + "_" + (i * 5 + j + 1) + ".jpg");
// 重新设置小方格的图片
picButtons[i * 5 + j].setIcon(new ImageIcon(showImage));
picButtons[5 * i + j].setLocation(70 + j * 90, 20 + i * 100);
// 小方格的文本内容 清空
picButtons[i * 5 + j].setText("");
}
}
}
/**
* 完成 空白格 和 小方格 进行位置的交换
* a b 空白格的坐标位置
* */
public void movePictureButton(int rx, int ry, String dir) {
for (int i = 0; i < picButtons.length - 1; i++) {
if (picButtons[i].getBounds().x == rx && picButtons[i].getBounds().y == ry) {
picButtons[i].movePictureButton(dir);
rectangle.setLocation(rx, ry);
break;
}
}
}
/**
* 完成图标的打乱
* */
boolean isListener = false;
public void upset() {// 打乱小方格
// 判断当前的小方格有没有被监听
if (!isListener) {
// 为每一个小方格添加监听
for (int i = 0; i < picButtons.length - 1; i++) {
picButtons[i].addMouseListener(this);
}
isListener = true;
}
//以第一个小方格的位置来判定,只要第一个小方格的位置没有发生改变就一直打乱小方格的位置
// for(int i=0;i<MainJFrame.buttonAreaJPanel.modIndex;i++) {
while (picButtons[0].getBounds().x <= 70 + MainJFrame.buttonAreaJPanel.modIndex * 90 || picButtons[0].getBounds().y <= 20 + MainJFrame.buttonAreaJPanel.modIndex * 100 ) {
//获取空白格的坐标
int rx = rectangle.getBounds().x;
int ry = rectangle.getBounds().y;
//定义一个 0 - 3 之间的随机整数,用来表示空白格上下左右移动的方向
int random = (int) (Math.random() * 4);
switch (random) {
case 0:
//当随机数为0 的时候 空白格水平向左移动一格
rx -= 90;
//和空白个重合的小方格向右水平移动一格
movePictureButton(rx,ry,"RIGHT");
break;
case 1:
//随机数为1时,空白格水平向右移动一格
rx += 90;
//和空白格重合的小方格向左水平移动一格
movePictureButton(rx,ry,"LEFT");
break;
case 2:
//随机数为2的时候 空白格垂直向下移动一格
ry += 100;
//和空白格重合的小方格垂直向上移动一格
movePictureButton(rx,ry,"UP");
break;
case 3:
//当随机数为3时,空白格垂直向下移动一格
ry -= 100;
movePictureButton(rx,ry,"DOWN");
break;
}
}
}
public void moveSwitch(int random,int rx,int ry) {
switch (random) {
case 0://随机数为0 --> 空白格 向左移动一格
//改变空白格的坐标
rx -= 90;
//和空白格重合的小方格进行向右移动一格
movePictureButton(rx, ry, "RIGHT");
break;
case 1: // ---> 空白格向右移动一格
//改变空格格的坐标
rx+=90;
//和空白格重合的小方格进行向左移动一格
movePictureButton(rx, ry, "LEFT");
break;
case 2:// ---> 向下
//改变空格格的坐标
ry+=100;
//和空白格重合的小方格进行向上移动一格
movePictureButton(rx, ry, "UP");
break;
case 3://向上
//改变空格格的坐标
ry-=100;
//和空白格重合的小方格进行向左移动一格
movePictureButton(rx, ry, "DOWN");
break;
}
}
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
//当鼠标按下时候 发生一些操作
// 1 获取被点击的对象 ---> 小方格
PictureButton p = (PictureButton) e.getSource();
// 2 获取当前被点击的小方格的 坐标
int px = p.getBounds().x;
int py = p.getBounds().y;
//3 .获取当前空方格的坐标
int rx = rectangle.getBounds().x;
int ry = rectangle.getBounds().y;
//4 判断 当前被点击的小方格 周围有没有空白格
if (px == rx && py - ry == 100) {//上边有空白格
//小方格 移动到上边
p.movePictureButton("UP");
}else if (px == rx && py - ry == -100) {//下边有空白格
//小方格移动到下边
p.movePictureButton("DOWN");
}else if (py == ry && px - rx == 90) {//左边有空白格
//小方格 左移
p.movePictureButton("LEFT");
}else if (py == ry && px - rx == -90) {//右边有空白格
// 小方格 右边移动
p.movePictureButton("RIGHT");
}else {
return;
}
// 5 当小方格移动之后 空白格跟着移动
rectangle.setLocation(px, py);// 将空白格的坐标设置为原来小方格的坐标
// 6 重绘
this.repaint();
// 7 当移动了一次 步数进行 + 1
PreviewAreaJPanel.stepCount++;
// 8更新 文本框中的步数的内容
ButtonAreaJPanel.step.setText("步数:"+PreviewAreaJPanel.stepCount);
// 9 当游戏完成的时候 弹出一格框框 显示 用了多少步
if (win()) {
//弹出框 显示信息
MainJFrame.buttonAreaJPanel.thread.stop();
long endTime = System.currentTimeMillis();
long cha = (endTime - MainJFrame.buttonAreaJPanel.beginTime)/1000;
JOptionPane.showMessageDialog(this, "胜利! 共用了:"+PreviewAreaJPanel.stepCount+"步,"+ cha+"秒");
// 复原游戏 取消 每个小方格的监听
for (int i = 0; i < picButtons.length-1; i++) {
picButtons[i].removeMouseListener(this);
}
isListener = false;
}
}
/**
* 判断是否游戏成功
* */
public boolean win() {
//遍历所有的方格 判断每个方格是不是在它应该在的位置
for (int i = 0; i < picButtons.length-1; i++) {
//获取方格的位置
int x = picButtons[i].getBounds().x;
int y = picButtons[i].getBounds().y;
//只要有一个方格不在自己的位置上就返回false
if ((y-20)/100*5 + (x-20)/90 != i) {
return false;
}
}
return true;
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
原文件以及源码下载链接:
链接:https://pan.baidu.com/s/19oe0AavtxNyCQfs8rbpK9A
提取码:3598
感谢支持!谢谢!