拼图小游戏
B站:【黑马程序员Java零基础视频教程_上部(Java入门,含斯坦福大学练习题+力扣算法题和大厂java面试题)】https://www.bilibili.com/video/BV17F411T7Ao?vd_source=902cae974409091c868112996a85ad70
一. 项目介绍



二. 界面设置和菜单搭建
1. 主界面分析

1.1 创建主界面 1


现在,运行 idea 就可以出现对应的三个界面
但是考虑到每个界面都有许多的业务逻辑代码,全都放在 Test 的 main 方法中是不可行的,当代码出现问题时,也不方便维护
在实际开发中,main 方法一般来讲是作为程序的启动入口,里面不会写什么业务逻辑代码
因此做出以下修改:

以上就是主界面搭建部分的内容
1.2 创建主界面 2


可以运行一下,看一下界面是否搭建完成
2. 菜单制作



GameJFrame.java
package com.game.ui;import javax.swing.*;public class GameJFrame extends JFrame {//JFrame表示界面、窗体,它的子类也表示界面、窗体//GameJFrame这个界面表示游戏的主界面//跟游戏相关的所有逻辑都写在这个类中//创建游戏主界面//构造方法:作用是创建对象时调用,名称必须和类名相同没有返回值类型public GameJFrame() {//设置界面宽高this.setSize(603, 680);//设置界面标题this.setTitle("拼图小游戏");//设置界面置顶this.setAlwaysOnTop(true);//设置界面居中this.setLocationRelativeTo(null);//设置关闭模式this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//创建整个菜单对象JMenuBarJMenuBar jMenuBar = new JMenuBar();//创建菜单上面的两个选项的对象 - 功能、关于我们JMenu FunctionJMenu = new JMenu("功能");JMenu AboutJMenu = new JMenu("关于我们");//创建每个选项对应的条目的对象 - 重新游戏、重新登陆、关闭游戏;说明JMenuItem replayJMenuItem = new JMenuItem("重新游戏");JMenuItem reLoginJMenuItem = new JMenuItem("重新登录");JMenuItem closeJMenuItem = new JMenuItem("关闭游戏");JMenuItem aboutJMenuItem = new JMenuItem("说明");//将每个选项对应的条目添加到对应的选项中FunctionJMenu.add(replayJMenuItem);FunctionJMenu.add(reLoginJMenuItem);FunctionJMenu.add(closeJMenuItem);AboutJMenu.add(aboutJMenuItem);//将选项添加到菜单中jMenuBar.add(FunctionJMenu);jMenuBar.add(AboutJMenu);//将菜单添加到界面中this.setJMenuBar(jMenuBar);//让游戏主界面显示出来this.setVisible(true);}
}
此时运行 App.java 就会出现如下的效果:

但有一个点:此时构造方法中的代码已经有很多了,如果以后出问题了,还要一行一行去里面找,那将会很麻烦,因此我们将每一部分抽取出来,变成独立的方法,修改如下:
package com.game.ui;import javax.swing.*;public class GameJFrame extends JFrame {//JFrame表示界面、窗体,它的子类也表示界面、窗体//GameJFrame这个界面表示游戏的主界面//跟游戏相关的所有逻辑都写在这个类中//创建游戏主界面//构造方法:作用是创建对象时调用,名称必须和类名相同没有返回值类型public GameJFrame() {//初始化游戏主界面initJFrame();//初始化菜单initJMenuBar();//让游戏主界面显示出来this.setVisible(true);}//初始化菜单private void initJMenuBar() {//创建整个菜单对象JMenuBarJMenuBar jMenuBar = new JMenuBar();//创建菜单上面的两个选项的对象 - 功能、关于我们JMenu FunctionJMenu = new JMenu("功能");JMenu AboutJMenu = new JMenu("关于我们");//创建每个选项对应的条目的对象 - 重新游戏、重新登陆、关闭游戏;说明JMenuItem replayJMenuItem = new JMenuItem("重新游戏");JMenuItem reLoginJMenuItem = new JMenuItem("重新登录");JMenuItem closeJMenuItem = new JMenuItem("关闭游戏");JMenuItem aboutJMenuItem = new JMenuItem("说明");//将每个选项对应的条目添加到对应的选项中FunctionJMenu.add(replayJMenuItem);FunctionJMenu.add(reLoginJMenuItem);FunctionJMenu.add(closeJMenuItem);AboutJMenu.add(aboutJMenuItem);//将选项添加到菜单中jMenuBar.add(FunctionJMenu);jMenuBar.add(AboutJMenu);//将菜单添加到界面中this.setJMenuBar(jMenuBar);}//初始化游戏主界面private void initJFrame() {//设置界面宽高this.setSize(603, 680);//设置界面标题this.setTitle("拼图小游戏");//设置界面置顶this.setAlwaysOnTop(true);//设置界面居中this.setLocationRelativeTo(null);//设置关闭模式this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);}
}
三. 添加图片


此时就完成了第一张图片的添加
但有一个问题:图片会被默认添加到中间的位置,那当我们添加多张图片时,它们就会重叠在一起,达不到我们想要的效果,因此引入了坐标的概念,来确定图片的位置:


修改后的代码如下:

运行效果如图:

现在第一张图片就添加完成了,让我们继续添加其他图片吧!


此处使用的循环嵌套来添加图片,效果如下:

由于图片中没有第16张,在添加第16张时,它也会加载一个 JLabel ,只不过它找不到要管理的图片,因此会加载一张空白
四. 打乱图片


五. 事件


1. ActionListener 动作监听
1.1 代码示例
Test3.java
package com.game.test;import javax.swing.*;
import java.awt.event.ActionEvent;public class Test3 {public static void main(String[] args) {JFrame jFrame = new JFrame();//设置界面的宽高jFrame.setSize(603, 680);//设置界面的标题jFrame.setTitle("事件演示");//设置界面置顶jFrame.setAlwaysOnTop(true);//设置界面居中jFrame.setLocationRelativeTo(null);//设置关闭模式jFrame.setDefaultCloseOperation(3);//取消默认居中方式jFrame.setLayout(null);//创建一个按钮对象JButton jtb = new JButton("点击我");//设置按钮的位置和宽高jtb.setBounds(0,0,100,50);//给按钮添加动作监听//jtb:组件对象,表示你要给哪个组件添加对象//addActionListener:表示我要给组件添加哪个事件监听(动作监听包含鼠标左键点击、空格)//参数:表示事件被触发后要执行的代码//jtb.addActionListener(new MyActionListener());jtb.addActionListener(new MyActionListener(){@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("按钮被点击了");}});//匿名内部类//把按钮添加到界面中jFrame.getContentPane().add(jtb);//设置界面可见jFrame.setVisible(true);}
}
MyActionListener.java
package com.game.test;import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;public class MyActionListener implements ActionListener {@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("按钮被点击了");}
}
1.2 代码示例
MyFrame.java
package com.game.test;import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;public class MyJFrame extends JFrame implements ActionListener {//创建一个按钮对象JButton jtb1 = new JButton("点我呀");//创建一个按钮对象JButton jtb2 = new JButton("再点我");public MyJFrame() {//设置界面的宽高this.setSize(603, 680);//设置界面的标题this.setTitle("事件演示");//设置界面置顶this.setAlwaysOnTop(true);//设置界面居中this.setLocationRelativeTo(null);//设置关闭模式this.setDefaultCloseOperation(3);//取消默认居中方式this.setLayout(null);//设置按钮的位置和宽高jtb1.setBounds(0,0,100,50);//给按钮添加事件监听jtb1.addActionListener(this);//设置按钮的位置和宽高jtb2.setBounds(100,0,100,50);//给按钮添加事件监听jtb2.addActionListener(this);//把按钮添加到界面中this.getContentPane().add(jtb1);this.getContentPane().add(jtb2);//设置界面可见this.setVisible(true);}@Overridepublic void actionPerformed(ActionEvent e) {//对当前的按钮进行判断//获取当前点击的按钮对象Object source = e.getSource();if(source == jtb1){jtb1.setSize(100,200);//改变按钮的大小}else if(source == jtb2){Random r = new Random();jtb2.setLocation(r.nextInt(500),r.nextInt(500));//改变按钮的位置}}
}
Test4.java
package com.game.test;public class Test4 {public static void main(String[] args) {new MyJFrame();}
}
2. MouseListener 鼠标监听



MyJFrame2.java
package com.game.test;import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;public class MyJFrame2 extends JFrame implements MouseListener {//创建一个按钮对象JButton jtb1 = new JButton("点我呀");public MyJFrame2() {//设置界面的宽高this.setSize(603, 680);//设置界面的标题this.setTitle("事件演示");//设置界面置顶this.setAlwaysOnTop(true);//设置界面居中this.setLocationRelativeTo(null);//设置关闭模式this.setDefaultCloseOperation(3);//取消默认居中方式this.setLayout(null);//设置按钮的位置和宽高jtb1.setBounds(0,0,100,50);//给按钮绑定鼠标事件jtb1.addMouseListener(this);//把按钮添加到界面中this.getContentPane().add(jtb1);//设置界面可见this.setVisible(true);}@Overridepublic void mouseClicked(MouseEvent e) {System.out.println("单击");}@Overridepublic void mousePressed(MouseEvent e) {System.out.println("按下不松");}@Overridepublic void mouseReleased(MouseEvent e) {System.out.println("松开");}@Overridepublic void mouseEntered(MouseEvent e) {System.out.println("划入");}@Overridepublic void mouseExited(MouseEvent e) {System.out.println("划出");}
}
Test4.java
package com.game.test;public class Test4 {public static void main(String[] args) {new MyJFrame2();}
}
3. KeyListener 键盘监听

MyJFrame3.java
package com.game.test;import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;public class MyJFrame3 extends JFrame implements KeyListener {public MyJFrame3() {//设置界面的宽高this.setSize(603, 680);//设置界面的标题this.setTitle("事件演示");//设置界面置顶this.setAlwaysOnTop(true);//设置界面居中this.setLocationRelativeTo(null);//设置关闭模式this.setDefaultCloseOperation(3);//取消默认居中方式this.setLayout(null);//给整个窗口添加键盘监听//调用者this:苯类对象,当前的界面对象,表示我要给整个界面添加监听//addKeyListener:表示要给本界面添加键盘监听//参数this:当事件被触发后,会执行本类中对应的代码this.addKeyListener(this);//设置界面可见this.setVisible(true);}@Overridepublic void keyTyped(KeyEvent e) {}@Override//细节1://细节2:public void keyPressed(KeyEvent e) {System.out.println("按下不松");}@Overridepublic void keyReleased(KeyEvent e) {System.out.println("松开按键");//获取键盘上每一个键盘的编号/*int code = e.getKeyCode();System.out.println(code);*/int code = e.getKeyCode();if(code==65){System.out.println("现在按的是A键");}else if(code==68){System.out.println("现在按的是D键");}}
}
Test4.java
package com.game.test;public class Test4 {public static void main(String[] args) {new MyJFrame3();}
}
六. 美化界面


GameJFrame.java
//初始化图片//添加图片的时候,根据二维数组中管理的数据进行添加private void initImage() {//路径分为两种://绝对路径:一定是从盘符开始的//相对路径:不是从盘符开始的,是相对当前项目而言的//细节:先加载的图片在上方,后加载的图片被塞在下方//先看内循环,再看外循环//外循环 - 把内循环重复执行了4次,使得在界面上添加4行图片for (int i = 0; i < 4; i++) {//内循环 - 在一行添加4张图片for (int j = 0; j < 4; j++) {//获取当前要加载的图片的编号int num = data[i][j];//创建一个JLabel对象,同时传入创建的ImageIcon图片对象JLabel jLabel = new JLabel(new ImageIcon("image\\animal\\animal3\\" + num + ".jpg"));//图片要先添加到JLabel中,才能通过JLabel把图片添加到界面中//指定图片位置jLabel.setBounds(105 * j + 83, 105 * i + 134, 105, 105);//给图片添加边框//0:让图片凸起来//1:让图片凹下去jLabel.setBorder(new BevelBorder(BevelBorder.LOWERED));//将管理容器(图片)添加到界面中this.getContentPane().add(jLabel);}}//添加背景图片JLabel background = new JLabel(new ImageIcon("image\\background.png"));//指定背景图片位置background.setBounds(40, 40, 508, 560);//将背景图片添加到界面中this.getContentPane().add(background);}七. 移动图片

1. 向上移动

先添加事件


2. 添加其他移动

3. 优化
在运行时我们会发现,当空白图片出现在边缘时,可能会报错 - 超出索引范围,因此我们需要对边界的情况做出判断:

4. 小结

八. 查看完整图片


优化路径:

九. 作弊码


十. 判断胜利



十一. 计步功能


十二. 菜单功能添加
1. 添加事件

2. 重新开始


3. 重新登陆
1. 关闭当前游戏界面 2. 打开登陆界面

4. 关闭游戏


5.关于我们



十三. 游戏打包成 .exe 安装包


