最近学习了java的基础知识,来写一个拼图小游戏练练手。
一、问题描述
本程序是一个由Java实现的拼图小游戏,在程序运行界面有一个菜单,设置了“更换图片”、“重新开始”、“退出游戏”和“关于”四个功能点。在游戏过程中,用户可以通过上、下、左、右键来控制拼图的移动方向,同时,程序运行窗口的右上角有一个计步设置,用来计算用户移动的步数,程序运行窗口左上角是一个完整的图片,以供用户参考。此外,还设置了一个作弊快捷键W,可以一键通关。
二、代码实现
创建一个类GameJFrame,继承父类JFrame,并实现KeyListener(键盘监听接口)、 ActionListener(动作监听接口)两个接口,以便后续操作。
initJFrame方法
初始化界面
private void initJFrame() {
//设置界面宽高
this.setSize(603, 680);
//界面标题
this.setTitle("拼图游戏 v1.0");
//界面置顶(置于其它界面之上)
this.setAlwaysOnTop(true);
//界面居中
this.setLocationRelativeTo(null);
//设置游戏关闭模式
this.setDefaultCloseOperation(3);
//取消默认的居中设置,只有取消了才会按照XY轴的形式添加组件
this.setLayout(null);
//添加键盘监听事件
this.addKeyListener(this);
}
initJMenuBar方法
初始化菜单
//创建选项下的条目对象(JMenuItem)
JMenuItem replay = new JMenuItem("重新开始");
JMenuItem close = new JMenuItem("关闭游戏");
JMenuItem official = new JMenuItem("公众号");
JMenuItem animal1 = new JMenuItem("狗");
JMenuItem animal2 = new JMenuItem("鹅");
JMenuItem animal3 = new JMenuItem("虎");
JMenuItem animal4 = new JMenuItem("熊");
private void initJMenuBar() {
//创建整个菜单对象(JMenuBar)
JMenuBar jMenuBar = new JMenuBar();
//创建菜单上面的两个选项对象(JMenu)
JMenu functionJMenu = new JMenu("功能");
JMenu AboutJMenu = new JMenu("关于");
//创建更换图片的JMenu
JMenu ChangeImg = new JMenu("更换图片");
//给条目绑定事件
replay.addActionListener(this);
close.addActionListener(this);
official.addActionListener(this);
animal1.addActionListener(this);
animal2.addActionListener(this);
animal3.addActionListener(this);
animal4.addActionListener(this);
//把animal1、2、3、4添加到更换图片中
ChangeImg.add(animal1);
ChangeImg.add(animal2);
ChangeImg.add(animal3);
ChangeImg.add(animal4);
//把更换图片的JMenu添加到功能的JMenu中
functionJMenu.add(ChangeImg);
//将条目添加到选项下(把JMenuItem放到JMenu里面)
functionJMenu.add(replay);
functionJMenu.add(close);
AboutJMenu.add(official);
//把JMenu放到JmenuBar里面
jMenuBar.add(functionJMenu);
jMenuBar.add(AboutJMenu);
//将JMenuBar放到整个JFrame界面中
this.setJMenuBar(jMenuBar);
}
initData方法
用于初始化数据。因为图片本身不能移动,所以我们将每个拼图编号,把图片的序号按顺序存放在一维数组中,然后打乱数组中的数据,将打乱后的结果赋值给二维数组。
(0,0) | (0,1) | (0,2) |
(1,0) | (1,1) | (1,2) |
(2,0) | (2,1) | (2,2) |
//初始化空白图片在二维数组中的坐标
int x = 0;
int y = 0;
private void initData() {
//定义一个一维数组
int[] tmparr = {0, 1, 2, 3, 4, 5, 6, 7, 8};
//打乱数组的顺序
Random r = new Random();
for (int i = 0; i < 9; i++) {
int random = r.nextInt(tmparr.length);
int tmp = tmparr[i];
tmparr[i] = tmparr[random];
tmparr[random] = tmp;
}
//将打乱后的数据添加到二维数组中
for (int i = 0; i < 9; i++) {
if (tmparr[i] == 0) {
//记录空白图片的坐标
x = i / 3;
y = i % 3;
}
data[i / 3][i % 3] = tmparr[i];
}
}
initImage方法
根据initData方法中打乱后的结果加载图片
//加载小块拼图
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
//获取要加载图片的序号
int imgnumber = data[i][j];
//创建一个图片(ImageIcon)对象
ImageIcon icon1 = new ImageIcon(path + "img-" + imgnumber + ".png");
//创建一个JLabel的对象(管理容器)
JLabel jLabel = new JLabel(icon1);
//指定图片位置
jLabel.setBounds(140 * j + 90, 140 * i + 148, 140, 140);
//让图片旁边有边界(0:让图片凸起来;1:让图片凹下去)
jLabel.setBorder(new BevelBorder(0));
//把管理容器添加到页面中
this.getContentPane().add(jLabel);
}
}
重写键盘监听接口中的keyReleased方法,以满足可以通过上、下、左、右键移动拼图和w键一键通关的需求。因为我们用二维数组存放了图片编号,所以把两个拼图块在二维数组中的位置互换即可完成移动拼图的操作。
//键盘监听:对上下左右进行判断
//上:38,下:40,左:37,右:39
int code = e.getKeyCode();
if (code == 37) {
//将空白块右边的图片往左移
System.out.println("向左移动");
if (y == 2) {
return;
}
data[x][y] = data[x][y + 1];
data[x][y + 1] = 0;
y++;
initImage();
//每移动一次,步数加1
++stepnumber;
} else if (code == 38) {
//将空白块下方的图片往上移
System.out.println("向上移动");
if (x == 2) {
return;
}
//将空白图片下方的拼图向上移动,与空白图片交换位置
data[x][y] = data[x + 1][y];
data[x + 1][y] = 0;
x++;
initImage();
//每移动一次,步数加1
++stepnumber;
} else if (code == 39) {
//将空白块左边的图片往右移
System.out.println("向右移动");
if (y == 0) {
return;
}
data[x][y] = data[x][y - 1];
data[x][y - 1] = 0;
y--;
initImage();
//每移动一次,步数加1
++stepnumber;
} else if (code == 40) {
//将空白块上面的图片往下移
System.out.println("向下移动");
if (x == 0) {
return;
}
data[x][y] = data[x - 1][y];
data[x - 1][y] = 0;
x--;
initImage();
//每移动一次,步数加1
++stepnumber;
} else if (code == 87) {
//作弊码,一键胜利
data = new int[][]{
{1, 2, 3},
{4, 5, 6},
{7, 8, 0}
};
}
重写动作监听接口的actionPerformed方法,使得菜单上的功能被点击后能触发相应事件
@Override
public void actionPerformed(ActionEvent e) {
//获取当前被点击的条目对象
Object obj = e.getSource();
if (obj == replay) {
System.out.println("重新开始");
StartChange();
} else if (obj == close) {
System.out.println("关闭游戏");
//关闭java虚拟机
System.exit(0);
} else if (obj == official) {
System.out.println("公众号");
//公众号以弹框形式出现
//创建一个弹框对象
JDialog jDialog = new JDialog();
//创建一个图片管理的容器对象JLabel
JLabel jLabel = new JLabel(new ImageIcon("E:\\IDEA-java\\JigsawGame\\src\\image\\background1.png"));
//设置位置和宽高
jLabel.setBounds(0, 0, 420, 420);
//将图片添加到弹框中
jDialog.getContentPane().add(jLabel);
//给弹框设置大小
jDialog.setSize(344, 344);
//让弹框置顶
jDialog.setAlwaysOnTop(true);
//让弹框居中
jDialog.setLocationRelativeTo(null);
//弹框不关闭,则无法进行其它操作
jDialog.setModal(true);
//让弹框显示出来
jDialog.setVisible(true);
} else if (obj == animal1) {
System.out.println("切换拼图为狗");
path = "E:\\IDEA-java\\JigsawGame\\src\\image\\Animal1\\";
StartChange();
} else if (obj == animal2) {
System.out.println("切换拼图为鹅");
path = "E:\\IDEA-java\\JigsawGame\\src\\image\\Animal2\\";
StartChange();
} else if (obj == animal3) {
System.out.println("切换拼图为虎");
path = "E:\\IDEA-java\\JigsawGame\\src\\image\\Animal3\\";
StartChange();
} else if (obj == animal4) {
System.out.println("切换拼图为熊");
path = "E:\\IDEA-java\\JigsawGame\\src\\image\\Animal4\\";
StartChange();
}
}
三、完整代码
package ui;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
//游戏主界面
//实现键盘监听接口和动作监听接口(鼠标监听接口的简化)
public class GameJFrame extends JFrame implements KeyListener, ActionListener {
//创建一个二维数组
int[][] data = new int[3][3];
//设置一个顺序完全正确的数组用于判断游戏是否胜利
int[][] win = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 0}
};
//初始化空白图片在二维数组中的坐标
int x = 0;
int y = 0;
//计步
int stepnumber = 0;
//记录当前展示的图片的路径
String path = "E:\\IDEA-java\\JigsawGame\\src\\image\\Animal2\\";
//创建选项下的条目对象(JMenuItem)
JMenuItem replay = new JMenuItem("重新开始");
JMenuItem close = new JMenuItem("关闭游戏");
JMenuItem official = new JMenuItem("公众号");
JMenuItem animal1 = new JMenuItem("狗");
JMenuItem animal2 = new JMenuItem("鹅");
JMenuItem animal3 = new JMenuItem("虎");
JMenuItem animal4 = new JMenuItem("熊");
public GameJFrame() {
//初始化界面
initJFrame();
//初始化菜单
initJMenuBar();
//初始化数据(打乱图片)
initData();
//初始化图片(根据打乱之后的结果去加载图片)
initImage();
//可视化
this.setVisible(true);
}
private void initData() {
//定义一个一维数组
int[] tmparr = {0, 1, 2, 3, 4, 5, 6, 7, 8};
//打乱数组的顺序
Random r = new Random();
for (int i = 0; i < 9; i++) {
int random = r.nextInt(tmparr.length);
int tmp = tmparr[i];
tmparr[i] = tmparr[random];
tmparr[random] = tmp;
}
//将打乱后的数据添加到二维数组中
for (int i = 0; i < 9; i++) {
if (tmparr[i] == 0) {
//记录空白图片的坐标
x = i / 3;
y = i % 3;
}
data[i / 3][i % 3] = tmparr[i];
}
}
private void initImage() {
//清空原本已经显示的所有图片
this.getContentPane().removeAll();
//提供一张完整的图片
//创建一个图片(ImageIcon)对象
ImageIcon iconall = new ImageIcon(path + "1.png");
//创建一个JLabel的对象(管理容器)
JLabel jLabelall = new JLabel(iconall);
//指定图片位置
jLabelall.setBounds(0, 0, 140, 140);
//把管理容器添加到页面中
this.getContentPane().add(jLabelall);
//判断是否胜利,显示胜利图标
if (ifwin()) {
//创建一个图片(ImageIcon)对象
ImageIcon iconwin = new ImageIcon("E:\\IDEA-java\\JigsawGame\\src\\image\\win.jpg");
//创建一个JLabel的对象(管理容器)
JLabel jLabelwin = new JLabel(iconwin);
//指定图片位置
jLabelwin.setBounds(200, 200, 230, 260);
//把管理容器添加到页面中
this.getContentPane().add(jLabelwin);
}
//显示步数
//创建一个JLabel的对象(管理容器)
JLabel step = new JLabel("步数:" + stepnumber);
//指定文字位置
step.setBounds(420, 100, 150, 70);
//把管理容器添加到页面中
this.getContentPane().add(step);
//加载小块拼图
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
//获取要加载图片的序号
int imgnumber = data[i][j];
//创建一个图片(ImageIcon)对象
ImageIcon icon1 = new ImageIcon(path + "img-" + imgnumber + ".png");
//创建一个JLabel的对象(管理容器)
JLabel jLabel = new JLabel(icon1);
//指定图片位置
jLabel.setBounds(140 * j + 90, 140 * i + 148, 140, 140);
//让图片旁边有边界(0:让图片凸起来;1:让图片凹下去)
jLabel.setBorder(new BevelBorder(0));
//把管理容器添加到页面中
this.getContentPane().add(jLabel);
}
}
//添加背景图片(先添加的图片在上方,后添加的在下方)
ImageIcon bg = new ImageIcon("E:\\IDEA-java\\JigsawGame\\src\\image\\background4.png");
JLabel backgroungimg = new JLabel(bg);
backgroungimg.setBounds(0, 0, 700, 700);
this.getContentPane().add(backgroungimg);
//刷新界面
this.getContentPane().repaint();
}
private void initJMenuBar() {
//创建整个菜单对象(JMenuBar)
JMenuBar jMenuBar = new JMenuBar();
//创建菜单上面的两个选项对象(JMenu)
JMenu functionJMenu = new JMenu("功能");
JMenu AboutJMenu = new JMenu("关于我们");
//创建更换图片的JMenu
JMenu ChangeImg = new JMenu("更换图片");
//给条目绑定事件
replay.addActionListener(this);
close.addActionListener(this);
official.addActionListener(this);
animal1.addActionListener(this);
animal2.addActionListener(this);
animal3.addActionListener(this);
animal4.addActionListener(this);
//把animal1、2、3、4添加到更换图片中
ChangeImg.add(animal1);
ChangeImg.add(animal2);
ChangeImg.add(animal3);
ChangeImg.add(animal4);
//把更换图片的JMenu添加到功能的JMenu中
functionJMenu.add(ChangeImg);
//将条目添加到选项下(把JMenuItem放到JMenu里面)
functionJMenu.add(replay);
functionJMenu.add(close);
AboutJMenu.add(official);
//把JMenu放到JmenuBar里面
jMenuBar.add(functionJMenu);
jMenuBar.add(AboutJMenu);
//将JMenuBar放到整个JFrame界面中
this.setJMenuBar(jMenuBar);
}
private void initJFrame() {
//设置界面宽高
this.setSize(603, 680);
//界面标题
this.setTitle("拼图游戏 v1.0");
//界面置顶(置于其它界面之上)
this.setAlwaysOnTop(true);
//界面居中
this.setLocationRelativeTo(null);
//设置游戏关闭模式
this.setDefaultCloseOperation(3);
//取消默认的居中设置,只有取消了才会按照XY轴的形式添加组件
this.setLayout(null);
//添加键盘监听事件
this.addKeyListener(this);
}
//重写键盘监听的方法
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
//如果游戏胜利,则此方法直接结束
if (ifwin()) {
return;
}
//键盘监听:对上下左右进行判断
int code = e.getKeyCode();
if (code == 37) {
System.out.println("向左移动");
if (y == 2) {
return;
}
data[x][y] = data[x][y + 1];
data[x][y + 1] = 0;
y++;
initImage();
//每移动一次,步数加1
++stepnumber;
} else if (code == 38) {
System.out.println("向上移动");
if (x == 2) {
return;
}
//将空白图片下方的图片向上移动,与空白图片交换位置
data[x][y] = data[x + 1][y];
data[x + 1][y] = 0;
x++;
initImage();
//每移动一次,步数加1
++stepnumber;
} else if (code == 39) {
System.out.println("向右移动");
if (y == 0) {
return;
}
data[x][y] = data[x][y - 1];
data[x][y - 1] = 0;
y--;
initImage();
//每移动一次,步数加1
++stepnumber;
} else if (code == 40) {
System.out.println("向下移动");
if (x == 0) {
return;
}
data[x][y] = data[x - 1][y];
data[x - 1][y] = 0;
x--;
initImage();
//每移动一次,步数加1
++stepnumber;
} else if (code == 87) {
//作弊码,一键胜利
data = new int[][]{
{1, 2, 3},
{4, 5, 6},
{7, 8, 0}
};
initImage();
}
System.out.println(code);
}
//判断游戏是否胜利(比较data数组和win数组是否完全相同)
public boolean ifwin() {
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
if (data[i][j] != win[i][j]) {
return false;
}
}
}
return true;
}
//重新开始、更换图片
public void StartChange() {
//计步归0
stepnumber = 0;
//重新打乱数据
initData();
//加载打乱数据后的图片
initImage();
}
//重写动作监听接口的方法
@Override
public void actionPerformed(ActionEvent e) {
//获取当前被点击的条目对象
Object obj = e.getSource();
if (obj == replay) {
System.out.println("重新开始");
StartChange();
} else if (obj == close) {
System.out.println("关闭游戏");
//关闭java虚拟机
System.exit(0);
} else if (obj == official) {
System.out.println("公众号");
//公众号以弹框形式出现
//创建一个弹框对象
JDialog jDialog = new JDialog();
//创建一个图片管理的容器对象JLabel
JLabel jLabel = new JLabel(new ImageIcon("E:\\IDEA-java\\JigsawGame\\src\\image\\background1.png"));
//设置位置和宽高
jLabel.setBounds(0, 0, 420, 420);
//将图片添加到弹框中
jDialog.getContentPane().add(jLabel);
//给弹框设置大小
jDialog.setSize(344, 344);
//让弹框置顶
jDialog.setAlwaysOnTop(true);
//让弹框居中
jDialog.setLocationRelativeTo(null);
//弹框不关闭,则无法进行其它操作
jDialog.setModal(true);
//让弹框显示出来
jDialog.setVisible(true);
} else if (obj == animal1) {
System.out.println("切换拼图为狗");
path = "E:\\IDEA-java\\JigsawGame\\src\\image\\Animal1\\";
StartChange();
} else if (obj == animal2) {
System.out.println("切换拼图为鹅");
path = "E:\\IDEA-java\\JigsawGame\\src\\image\\Animal2\\";
StartChange();
} else if (obj == animal3) {
System.out.println("切换拼图为虎");
path = "E:\\IDEA-java\\JigsawGame\\src\\image\\Animal3\\";
StartChange();
} else if (obj == animal4) {
System.out.println("切换拼图为熊");
path = "E:\\IDEA-java\\JigsawGame\\src\\image\\Animal4\\";
StartChange();
}
}
}