使用Java编写拼图小游戏主要包括:
初始化界面初始化菜单初始化数据初始化图片其他必要功能的实现
这几部分。在进行细分时包括了菜单的搭建,添加图片,打乱图片位置,让图片移动起来,查看完整图片,设置作弊码,判断胜利,实现计步功能,让整个界面显示出来等,下面来进行详细的讲解。
注:在进行代码的编程时,最好是把相同功能的代码写到同一个方法中,能够增强代码的阅读性以及方便之后修改代码
初始化界面
在初始化界面时要获取到界面,由于类中继承了JFrame父类,获得有默认界面窗口,可以使用this表示本类
设置界面的标题,设置界面置顶(防止其他界面覆盖游戏界面),设置界面居中(使界面出现在屏幕中央,否则界面会默认出现在屏幕左上角),
设置关闭模式,关闭模式使用setDefaultCloseOperation()方法,一共有三种为:括号里面分别填写DO_NOTHING_ON_CLOSE(可以用0代替),表示什么都不做,既不关闭界面,也不停止运行;填写HIDT_ON_CLOSE(可以用1代替)表示默认的窗口关闭操作,可以关闭窗口,不能停止运行;填写DISPOSE_ON_CLOSE(可以用2代替)表示如果开启了多个界面,只有关闭了最后的界面,虚拟机才会停止运行;填写EXIT_ON_CLOSE(可以用3代替),表示只要关闭了此界面,整个虚拟机就会停止
取消组件的默认居中放置,,由于需要在界面中进行游戏操作,还要在界面中添加一个键盘监听事件
代码为:
private void initJFrame() {//设置界面的宽高this.setSize(603,680);//设置界面的标题this.setTitle("拼图单机版 v1.0");//设置页面置顶this.setAlwaysOnTop(true);//设置页面居中this.setLocationRelativeTo(null);//设置关闭模式this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//取消默认居中放置的方法//只有取消了默认居中放置,才会按照xy轴的方式添加组件this.setLayout(null);//给整个界面添加键盘监听事件this.addKeyListener(this);}
初始化菜单 先创建JMenuBar对象再创建JMenu对象再创建JMenuItem对象把JMenuItem放到JMenu里面,再把JMenu放到JMenuBar里面最后把JMenuBra添加到整个JFrame界面中
JFrame:最外层的窗体(当创建JFrame对象时,就会出现窗口)
JMenuBra:最上层的菜单(:java中专门用来描述菜单的类)
JLabel:文字和图片的管理容器
添加图片
图片对应的类叫做ImageIcon,一张图片对应一个ImageIcon的图像,如果要给图片设置宽高,位置,边框等,需要把图片放到JLable中
在设置图片的位置时,使用setBound方法,以界面左上角为原点,水平向右为x轴,竖直向下为y轴,通过更改x,y的坐标可以改变图片的位置
代码为:
private void initImage(){//创建一个图片ImageIcon的对象ImageIcon icon = new ImageIcon("图片的详细位置");//创建一个JLable的对象(管理容器)JLable jLable = new JLable(icon);//表示这个管理容器就管理icon这个对象//可以简写://JLable jLable = new JLable(new ImageIcon("图片详细地址"));//指定图片的位置jLable.setBounds(105, 0, 105, 105);//分别表示x,y轴,宽,高//把管理容器添加到界面中this.add(jLable);//表示添加jLable这个图片}
由于每添加一张图片都要创建一个ImageIcon,过于繁琐,可以使用循环的方法简化代码
在添加了图片后,图片会默认放在界面正中央的位置,需要先使用setLayout方法,取消图片的默认居中放置,才能更改图片的位置
在初始化图片时,先使用getContentPane方法获取隐藏容器(在创建JFrame对象时自动创建出来了),用来装载组件,在调用add方法把图片添加到隐藏容器中
找不到的图片系统会默认以空白补上
代码为:
private void initImage(){int number = 1;//外循环--把内循环重复执行了四次for(int i = 0; i<4; i++){for(int j = 0; j<4; j++){//创建一个JLabel对象(管理容器)//详细位置中number的使用:把图片前的数字变成"+number+"JLabel jlabel = new JLabel(new ImageIcon("详细位置"));//指定图片位置jLabel.setBounds();//括号里面填x轴,y轴,宽,高对应的数据//先获取到隐藏的容器,再把管理容器添加给隐藏容器中this.getContentPane().add(jLabel);//添加一次之后number需要自增number++; } }
打乱图片的位置
原理:每张图片代表一个数字,空白图片代表0,把这些数字组成一个数组,打乱这个数组中的数字,同时把这些数字分成4组,组成一个二维数组,根据数组定义图片
private void initData() {int[] arr ={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};//打乱数组中的数据顺序//便利数组,得到每一个元素,拿着每一个元素跟随机索引上地数据进行交换Random r = new Random();for(int i = 0; i < arr.length; i++) {int index = r.nextInt(arr.length);//打乱图片顺序int a = arr[index];arr[index] = arr[i];arr[i] = a;}//便利一维数组得到每一个元素,把每一个元素依次添加到二维数组中for(int i = 0; i < arr.length; i++) {arrs[i / 4][i % 4] = arr[i];}
让图片移动起来
事件:可以被组件识别的操作,当组件做了某些事以后,就会执行对应的代码
事件中的三个要素:
事件源:按钮,图片,窗体等事件:(对事件源进行的操作)某些操作:如,鼠标单击,鼠标划入等绑定监听:事件源发生了某些事件,就会执行某段代码。。监听有KeyListener(键盘监听),MouseListener(鼠标监听),ActionListener(动作监听)
通过给组件绑定相应的监听事件,当组件出现了相应动作,就会执行相应代码
绑定监听事件是一个接口,在实现接口后要重写接口内所有的方法
//松开按键时会调用这个方法@Overridepublic void keyReleased(KeyEvent e) {//为了避免游戏结束后仍然可以使图片一定,需要进行判断//如果游戏胜利,这个方法直接结束,不能再执行下面的移动代码if(victory()){//return的作用,返回结果,结束方法return;}//对上下左右进行判断//左:37 上:38 右:39 下:40int code = e.getKeyCode();if(code == 37){//进行判断,空白图片在最最右方时,直接结束方法if (y == 3){return;}// 把空白方块的数字往右移动,左边的数字互换位置//x,y-1;表示另一个数字//把带图的数字赋值给不带图的arrs[x][y] = arrs[x][y+1];//之后原来带图的数字的标号变成0arrs[x][y+1] = 0;y++;//每移动一次,计步器就自增一次step++;initImage();System.out.println("向左移动");}else if(code == 38){if (x == 3){return;}arrs[x][y] = arrs[x+1][y];arrs[x+1][y] = 0;//每移动一次,计步器就自增一次step++;//x++表示在后来空白图片的位置xyx++;//调用方法,按照最新的数字加载图片initImage();System.out.println("向上移动");}else if(code == 39){if(y == 0){return;}arrs[x][y] = arrs[x][y-1] ;arrs[x][y-1] = 0;//每移动一次,计步器就自增一次step++;y--;initImage();System.out.println("向右移动");}else if(code == 40){if(x == 0){return;}arrs[x][y]= arrs[x-1][y];arrs[x-1][y] = 0;//每移动一次,计步器就自增一次step++;x--;initImage();System.out.println("向下移动");
美化界面
调整游戏界面的位置,给游戏界面添加背景颜色,添加游戏界面的边框
调整游戏界面的位置:指定游戏的位置时,在xy轴的位置添加像素,能把该图片再进行移动
原来:jLable.setBounds(105*j, 105*i, 105, 105);移动:jLable.setBounds(105*j+83, 105*i+134, 105, 105);
设置背景图片
添加图片在初始化图片中添加
代码加载图片时先加载的图片在上方(最外层),后加载的图片在下方(内层)
//添加背景图片JLabel background = new JLabel(new ImageIcon("背景图片的位置"));background.setBounds(40, 40, 508, 560);//把背景图片添加到界面当中//this表示当前的界面对象this.getContentPane().add(background);
给图片添加边框
调用setBoder方法,Border是一个接口,规定边框的规则,BevelBorder表示斜面边框
0表示让图片凸起来,RAISED
1表示让图片凹进去,LOWERED
先new一个斜面边框的对象,BevelBorder.LOWERED和BevelBorder.RAISED可以用1和0表示
jLable.setBorder(new BevelBOrder(BevelBorder.LOWERED));
查看完整图片,作弊码和判断胜利
查看完整图片:按住A不松,显示完整图片,松开A显示随机打乱的图片
在重写的键盘监听事件中进行
@Overridepublic void keyPressed(KeyEvent e) {int code = e.getKeyCode();if(code == 65){//把界面中的所有图片全部删除this.getContentPane().removeAll();//加载第一张完整图片JLabel all = new JLabel(new ImageIcon("完整图片的位置"));//设定图片的位置all.setBounds(83, 134, 420, 420);//调用出隐藏容器并把图片放进去this.getContentPane().add(all);
设置作弊码:
在释放按键执行的代码中,重新定义二维数组,二维数组为正确排序的顺序,,如果释放了w键,则按照更改后的二维数组重新加载图片,w代表的数据为87
if(code == 87){/*获取如果释放w键后,直接通关* 重新给arrs这个二维数组赋值* 二维数组为固定的* 之后重新加载图片*/arrs = new int[][]{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,0}};//按照新定义的数组重新加载图片initImage();
设置通关胜利画面:
定义一个正确的二维数组win(在成员变量的地方)
在加载图片之前,先判断一下二维数组中的数字跟win数组中是否相同(定义一个方法进行判断)如果相同,显示正确图标如果不同则不显示
定义正确的二维数组(在成员变量的位置)
int[][] win = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,0}};
重新定义一个方法,用来判断arrs数组与win数组是否相同
//判断目前数组中的数据与win数组中的相同//如果相同,返回true。否则返回falsepublic boolean victory(){for(int i = 0; i<arrs.length; i++){//i:依次表示二维数组arrs里面的索引//arrs[i]表示每一个一维数组for(int j = 0; j<arrs[i].length; j++){if(arrs[i][j] != win[i][j]){//只要有一个数据不一样,就返回falsereturn false;}}}//循环结束以后,数组遍历比较完,数据全部一样则返回truereturn true;}
在初始化图片的位置
添加胜利的图标
//调用比较数组的方法,对结果进行判断if(victory() == true){//显示胜利的图标//创建一个JLable对象JLabel winJLabel = new JLabel(new ImageIcon("jigsaw\\素材\\image\\win.png"));//给胜利的图标设置位置winJLabel.setBounds(203,283, 197, 73);//让胜利的图标显示出来this.getContentPane().add(winJLabel);}
计步功能
定义一个变量,统计已经玩了多少步(在成员中定义变量,如果定义在方法中,变量每在方法调用后都会重新使用定义值)每次按上下左右的时候计步器自增一次
定义成员变量表示计步器,在初始化图片中设置计步器的位置宽高等,在KeyReleased方法中设置使计步器自增
在initImage中进行定义:
//把计步器加载到界面//创建一个JLabel对象JLabel stepCount = new JLabel("步数:"+step);//设置位置和宽高stepCount.setBounds(50, 30, 100, 20);//使图片显示出来this.getContentPane().add(stepCount);
重新游戏
给重新游戏绑定事件:可以绑定MouseListener鼠标监听事件(包含点击,划入,划出,松开,单击,,单击是点击松开后自动补充的),也可以绑定ActionListener动作监听事件(包含鼠标点击和空格两个功能)
:::实现接口,重写方法:在GameJFrame类中,实现ActionListener接口,并重写接口中的抽样方法
重新打乱二维数组中的数字(重新游戏使用)加载图片计步器清零
if(obj == replayItem){//计步器清零step = 0;//再次打乱二维数组中的数据initData();//重新加载图片initImage();System.out.println("重新游戏");
关闭游戏
给关闭游戏绑定事件结束虚拟机,关闭所有
else if(obj == closeItem){System.out.println("关闭游戏");//直接关闭虚拟机System.exit(0);
完整代码::
package 验证;/*需求::* 设置更换图片的事件* 给条目绑定这个事件*/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;public class GameJFrame extends JFrame implements KeyListener, ActionListener {public static void main(String[] args){new GameJFrame();}//JFrame表示界面或者窗口//子类也表示界面或者窗口//规定:GameJFrame这个界面就是游戏的主界面//以后跟游戏相关的所有逻辑写在这个类中//定义一个二维数组,存储正确的数字int[][] win = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,0}};// 创建选项下条目的对象,,,创建对象的代码JMenuItem replayItem = new JMenuItem("重新游戏");JMenuItem closeItem = new JMenuItem("关闭游戏");JMenuItem accountItem = new JMenuItem("公众号");//定义一个变量,记录当前显示的图片路径//如果要更改图片可以直接更改path记录的值String path = "jigsaw\\素材\\image\\animal\\animal3\\" ;//定义一个方法,表示需要随机抽取的图片//只需要获取路径中的随机图片//需要用到动作监听事件//记录空白放块在二维数组中的位置int x = 0;int y = 0;//定义一个变量用来统计步数int step = 0;// 创建一个二维数组//用来管理数据,在加载图片是要根据二维数组中的数据进行加载//在下面两个方法里面都会用到数组,因此应该写在成员位置int[][] arrs = new int[4][4];//写一个构造方法public GameJFrame(){//初始化界面initJFrame();//初始化菜单initJMenuBra();//初始化数据(打乱)initData();//初始化图片(根据打乱的结果加载图片)initImage();//ran让界面显示出来,建议写在最后this.setVisible(true);}//初始化数据,采用打乱的方式//初始化图片//添加图片是要按照二维数组中管理的数据添加图片private void initData() {//定义一个一维数组int[] arr ={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};//打乱数组中的数据顺序//便利数组,得到每一个元素,拿着每一个元素跟随机索引上地数据进行交换Random r = new Random();for(int i = 0; i < arr.length; i++) {//获取到随机索引int index = r.nextInt(arr.length);//拿遍历到地每一个数据跟随机索引地数据进行交换,// 这样即便获得的随机数有一样的数// 交换顺序后相同数的个数不会改变// 造成的结果只是数组中同一个数进行了多次交换// 交换之后顺序打乱int a = arr[index];arr[index] = arr[i];arr[i] = a;}//便利一维数组得到每一个元素,把每一个元素依次添加到二维数组中for(int i = 0; i < arr.length; i++) {if (arr[i] == 0){x = i/4;y = i%4;}arrs[i / 4][i % 4] = arr[i];}}//初始化图片方法,在上面进行调用//由于上面打乱了二维数组的数据,在添加图片的时候就要按照二维数组管理的数据添加图片private void initImage() {//路径的分类//绝对路径:一定从盘符开始。C:\//相对路径:不是从盘符开始的,//相对路径相对当前的项目而言,如:aaa\\bbb//在当前项目下,去找aaa文件夹,里面再找bbb文件夹//清空原本出现的图片this.getContentPane().removeAll();//调用比较数组的方法,对结果进行判断if(victory() == true){//显示胜利的图标//创建一个JLable对象JLabel winJLabel = new JLabel(new ImageIcon("jigsaw\\素材\\image\\win.png"));//给胜利的图标设置位置winJLabel.setBounds(203,283, 197, 73);//让胜利的图标显示出来this.getContentPane().add(winJLabel);}//把计步器加载到界面//创建一个JLabel对象JLabel stepCount = new JLabel("步数:"+step);//设置位置和宽高stepCount.setBounds(50, 30, 100, 20);//使图片显示出来this.getContentPane().add(stepCount);// int number = 1;//加载图片时,先加载的图片在上方(外层),//后加载的图片在下方(内层)//按照二维数组的数字添加图片//外循环,表示要添加四行图片for (int j = 0; j<4; j++) {//内循环,表示在一行要添加4行图片for (int i = 0; i<4; i++){//获取加载图片的序号int nums = arrs[j][i] ;//创建JLabel对象(管理容器),并把图片放进去JLabel jLabel = new JLabel(new ImageIcon(path+""+nums+".jpg"));//指定图片的位置jLabel.setBounds(105*i+83, 105*j+134, 105, 105);//给图片添加边框//0表示让图片凸起来RAISED//1表示让图片凹进去LOWEREDjLabel.setBorder(new BevelBorder(BevelBorder.RAISED));//把管理容器添加到隐藏界面中this.getContentPane().add(jLabel);}}//添加背景图片JLabel background = new JLabel(new ImageIcon("jigsaw\\素材\\image\\background.png"));background.setBounds(40, 40, 508, 560);//把背景图片添加到界面当中//this表示当前的界面对象this.getContentPane().add(background);//刷新界面this.getContentPane().repaint();}//初始化菜单private void initJMenuBra() {//创建整个菜单对象JMenuBar JMenuBra = new JMenuBar();//创建菜单上两个选项的对象(功能 关于我们)设置选项JMenu functionJMenu = new JMenu("功能");JMenu aboutJMenu = new JMenu("关于我们");JMenu changeImage = new JMenu("更换图片");//把每一个选项下的条目接收到选项中functionJMenu.add(replayItem);//使用add方法调用replayItemfunctionJMenu.add(closeItem);aboutJMenu.add(accountItem);//给条目绑定事件//使用对象调用方法执行this本类中的代码replayItem.addActionListener(this);closeItem.addActionListener(this);accountItem.addActionListener(this);//把菜单中的两个选项添加到菜单当中JMenuBra.add(functionJMenu);JMenuBra.add(aboutJMenu);JMenuBra.add(changeImage);//给整个界面设置菜单//使用方法//this表示当前的界面对象,调用set方法'this.setJMenuBar(JMenuBra);}//初始化界面private void initJFrame() {//设置界面的宽高this.setSize(603,680);//设置界面的标题this.setTitle("拼图单机版 v1.0");//设置页面置顶this.setAlwaysOnTop(true);//设置页面居中this.setLocationRelativeTo(null);//设置关闭模式// this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//取消默认居中放置的方法//只有取消了默认居中放置,才会按照xy轴的方式添加组件this.setLayout(null);//给整个界面添加键盘监听事件this.addKeyListener(this);}@Overridepublic void keyTyped(KeyEvent e) {}//按下不松时调用的方法@Overridepublic void keyPressed(KeyEvent e) {int code = e.getKeyCode();if(code == 65){//把界面中的所有图片全部删除this.getContentPane().removeAll();//加载第一张完整图片JLabel all = new JLabel(new ImageIcon(path+"all.jpg"));//设定图片的位置all.setBounds(83, 134, 420, 420);//调用出隐藏容器并把图片放进去this.getContentPane().add(all);//加载背景图片JLabel background = new JLabel(new ImageIcon("jigsaw/素材/image/background.png"));//设置背景图片的位置background.setBounds(40, 40, 508, 560);//把背景图片添加到界面中this.getContentPane().add(background);//刷新界面this.getContentPane().repaint();}}//松开按键时会调用这个方法@Overridepublic void keyReleased(KeyEvent e) {//为了避免游戏结束后仍然可以使图片一定,需要进行判断//如果游戏胜利,这个方法直接结束,不能再执行下面的移动代码if(victory()){//return的作用,返回结果,结束方法return;}//对上下左右进行判断//左:37 上:38 右:39 下:40int code = e.getKeyCode();if(code == 37){//进行判断,空白图片在最最右方时,直接结束方法if (y == 3){return;}// 把空白方块的数字往右移动,左边的数字互换位置//x,y-1;表示另一个数字//把带图的数字赋值给不带图的arrs[x][y] = arrs[x][y+1];//之后原来带图的数字的标号变成0arrs[x][y+1] = 0;y++;//每移动一次,计步器就自增一次step++;initImage();System.out.println("向左移动");}else if(code == 38){if (x == 3){return;}//把空白方块下方的数字往上移动// xy表示空白方块//x+1,y表示空白方块下方的数字//把空白方块下方的数字赋值给空白方块arrs[x][y] = arrs[x+1][y];arrs[x+1][y] = 0;//每移动一次,计步器就自增一次step++;//x++表示在后来空白图片的位置xyx++;//调用方法,按照最新的数字加载图片initImage();System.out.println("向上移动");}else if(code == 39){if(y == 0){return;}//令左边的图片右移//把左边图片的标号与0互换//再次记录空白图片的位置arrs[x][y] = arrs[x][y-1] ;arrs[x][y-1] = 0;//每移动一次,计步器就自增一次step++;y--;initImage();System.out.println("向右移动");}else if(code == 40){if(x == 0){return;}//令空白图片上面的图片下移//令空白图片与其上方图片的标号互换//再次记录空白图片的数组表示,由于只有y变了,只改变y的值就可以了arrs[x][y]= arrs[x-1][y];arrs[x-1][y] = 0;//每移动一次,计步器就自增一次step++;x--;initImage();System.out.println("向下移动");}else if(code == 65){initImage();}else if(code == 87){/*获取如果释放w键后,直接通关* 重新给arrs这个二维数组赋值* 二维数组为固定的* 之后重新加载图片*/arrs = new int[][]{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,0}};//按照新定义的数组重新加载图片initImage();}}//判断目前数组中的数据与win数组中的相同//如果相同,返回true。否则返回falsepublic boolean victory(){for(int i = 0; i<arrs.length; i++){//i:依次表示二维数组arrs里面的索引//arrs[i]表示每一个一维数组for(int j = 0; j<arrs[i].length; j++){if(arrs[i][j] != win[i][j]){//只要有一个数据不一样,就返回falsereturn false;}}}//循环结束以后,数组遍历比较完,数据全部一样则返回truereturn true;}@Overridepublic void actionPerformed(ActionEvent e) {//获取当前的条目对象Object obj = e.getSource();//判断当前点击的是谁if(obj == replayItem){/** 重新加载图片中用到了计步器,应该把计步器清零放到重新加载图片的上面* 打乱数组中的代码要进行改动:* 原来0序号的数组没有进行赋值,更改为进行赋值的情况,防止出现重新开始加载图片时没有空白图片的现象*///计步器清零step = 0;//再次打乱二维数组中的数据initData();//重新加载图片initImage();System.out.println("重新游戏");}else if(obj == closeItem){System.out.println("关闭游戏");//直接关闭虚拟机System.exit(0);}else if(obj == accountItem){System.out.println("公众号");/*以弹框的形式出现* 弹框里面加了一张公众号* *///创建一个弹框对象JDialogJDialog jDialog = new JDialog();//创建一个管理图片的容器对象JLabelJLabel jLabel = new JLabel(new ImageIcon("jigsaw\\素材\\image\\about.png"));//设置位置和宽高,相对有弹框而言的,表示要把jLabel放在弹框那里jLabel.setBounds(0, 0, 258, 258);//把图片添加到弹框当中jDialog.getContentPane().add(jLabel);//给弹框设置大小jDialog.setSize(344,344);//让弹框置顶,diao调用方法AlwaysOnTop,kuo括号里面填写truejDialog.setAlwaysOnTop(true);//让弹框居中jDialog.setLocationRelativeTo(null);//弹框不关闭则无法操作下面的界面jDialog.setModal(true);//让弹框显示出来jDialog.setVisible(true);}}}