基于Java的拼图游戏

时间:2022-01-16 16:46:04


项目需求:

  1. 用户可通过目录,选定要进行拼图的照片,照片经过处理后,被分割为3*3的小块;

  2. 将其中的小块放置到3*3的框中,其中的最右下角留白;

  3. 按上下左右方向键,移动方框中的照片小块,直到拼接出原始的图像,游戏结束;

 

已有资料:

http://blog.sina.com.cn/s/blog_5e3ab00c0100igqh.html

http://blog.sina.com.cn/s/blog_5e3ab00c0100ipz6.html

参考上述的源代码来完成自己的设计。

 

任务表:

  1. 完成对话框定位目录的功能(优先级低);

  2. 对定位到的图片进行标准化,分割处理(优先级高)

  3. 将分割后的图片贴在图文框中(优先级高);

  4. 添加对键盘的监听,重绘图像,形成移动的效果;每移动一次判断是否完成拼图(优先级高);

  5. 如何判断当前初始的拼图是否可拼?(优先级高)

 

任务2

需要将图片进行分割,参考的源代码目录如下:

基于Java的拼图游戏

按照代码的名字,我们可以到Split中去看看是否有我们需要的内容,发现该类中的divid方法,正好完成对图片的分割工作,该部分代码可以拿来用。

public BufferedImage[][] divid(int type)

 {

 try

 {

  if(filename == null)

   return null;

 

  BufferedImage image = ImageIO.read(new File(path));

  int len = level[type];

  int cal = image.getWidth() / len;

  int row = image.getHeight() / len;

  BufferedImage [][] subimage = new BufferedImage[row][cal];

 

  for (int i = 0; i < row; i++)

   for (int j = 0; j < cal; j++)

    subimage[i][j] = image.getSubimage(j*len, i*len, len, len);

 

  return subimage;

 }

 catch (Exception e)

 {

  return null;

 }

 }

}

 

任务3:将分割后的图片贴在图文框中(优先级高);

此处是要对分割后的image进行处理,暂时我们跟着参考源代码看看,他是怎么做的:

public void menuNewClick()

 {

 Split sp = Split.get();

 BufferedImage [][] image;

 if(!sp.set(getFilename()) || (image = sp.divid(getType())) == null)

 {

  JOptionPane.showMessageDialog(null, "File"+getFilename()+" not exists!\nPlease select again~");

  return;

 }

 

 startGame();

 this.setSize(fWidth, fHeight);

 this.setVisible(true);

 intlen = Split.level[getType()];

 introw = image.length;

 intcal = image[0].length;

 

 gOver = new GameOver(this);

 JButton [][] button = new JButton[row][cal];

 Matrix matrix = new Matrix(button, panel[0], len, gOver);

 matrix.init(image);

 

从上述的代码可见,对于image分割方法的调用在image = sp.divid(getType())),而对于image的进一步处理就落在matrix部分的init方法中。

 

 

public void init(BufferedImage [][] image)

 {

  

  icon = new ImageIcon(image[d/cal][d%cal]);

  button[i/cal][i%cal].setIcon(icon);

 }

 }

从上述的代码可见,其主要的思路:将分割得到的image转化成为icon对象,然后再将icon对象设置给button;而在matrix的初始化函数中:

public Matrix(JButton [][] b, JPanel p, intlen, GameOver g){

 for(int i = 0; i < row; i++)

  for (int j = 0; j < cal; j++)

  {

   button[i][j] = new JButton();

   button[i][j].setBounds(j*len, i*len, len, len);

   button[i][j].addActionListener(new ButtonClick(button, pint, matrix, i,j, gOver));

   panel.add(button[i][j]);

  }

 }

}

上述的代码给我们的设计指出大方向:将整个的panel划分为3*3的区域,其中的依次无缝隙放置9个按钮;然后再将我们的图片分割为3*3的小块,再将每个小块转化为icon类型贴在每个按钮的表面,如此完成游戏的静态设置。

 

 

任务4:添加对键盘的监听,重绘图像,形成移动的效果;每移动一次判断是否完成拼图(优先级高);

 

本任务总体可分解得子任务:

    1. 如何产生移动的效果?

    2. 如何判断拼图是否完成?

 

按照任务3中的总体设计,实现移动的效果:要么移动按钮在panel中的位置,要么重置按钮上的icon;上述只是粗略的技术路线,还需要具体到:

每条路线的具体实现难度;

该技术路线与后续任务协同上的难易程度;

 

启发:技术路线的除了考虑自身的难易程度,还需要考虑与其他技术的协同难度;

 

先不想动手?那就在脑子里跑火车,看看上述两条技术路线的技术难度:

如果采用重置icon的方式,那就是每次沿着按键方向,将前者的icon置为空白的即可;而判断是否已经处于拼图完成状态,只需要检查每个按钮上的icon是否按顺序排列;

如果采用移动按钮的方式,只不过是带着icon一起移动而已,因为我们要的只是icon的移动,所以两者方式的对比,更合理的是第一种方式。

 

任务5.2:如何判断拼图当前是否已经完成?

此处需要记录各个icon所对应的imag变化的情况,也即记录当前的icon重置后,需要记录其位置的变化。此处的思路可以有:1.根据icon身上附加的属性,根据icon身上的信息判断当前的布局是否满足拼图完成的条件;2.建立icon位置相对应的数据结构,比如3*3的二维数组,给每个icon设定一个初始的编号:

基于Java的拼图游戏

如果第8个位置对应的icon,移动到空格的位置,那么对应的二维数组变为:

基于Java的拼图游戏

拥有这样的对应icon位置的二维数组,那么判断当前的拼图是否出于完成状态就简单很多了,只要查看下二维数组中的数据是否按序排列即可。

 

任务5:判断初始化的拼图是否可拼?

我们百度下,看看网上是否有相关资料:

http://www.cnblogs.com/idche/archive/2012/04/25/2469516.html

http://blog.csdn.net/tailzhou/article/details/3002442

数学终于派上用场,使用到数学是大学一年级的线性代数。此处具体的细节与数学证明略过。主要的结论:二维数组中数的逆序对,如果逆序对为偶数,说明可拼图;否则,就不可以。那剩下的就只是用两层循环来判断下逆序对的数量。

 

按照上述的设计,拼图的技术框架构建完成。




拼图游戏-实现方案


 


以下罗列技术方案中的主要任务:


  1. 完成对话框定位目录的功能(优先级低);

  2. 对定位到的图片进行标准化,分割处理(优先级高)

  3. 将分割后的图片贴在图文框中(优先级高);

  4. 添加对键盘的监听,重绘图像,形成移动的效果;每移动一次判断是否完成拼图(优先级高);

  5. 如何判断当前初始的拼图是否可拼?(优先级高)


 


实现1


任务2中对图形的处理和分割可以独立到一个类中,如参考源代码中的处理方式,独立为handlePic类;


主要方法:完成对该图像的分割,并且返回分割后的对象;


 


实现2


判断当前的拼图是否已经完成,使用二维数组,该二维数组还需要提供必要的方法,所以将其设计为独立的类Matrix类;



主要方法1:提供二维数组中的数据交换;


主要方法2:检查当前的二维数组,查看是否已经处于拼图完成状态;


主要方法3:提供初始(可拼接)的二维数组,根据该二维数组来完成分割后image到按钮的对应;


 


实现3


界面的创建:创建一个主界面类,其中添加Panel以及3*3个的button对象,再调用handlePic类和Matrix类完成icon的初始化;将类的名字暂时命名为puzzle



实现4


对于按键或者鼠标的响应:出于简单,我们只实现关于鼠标点击按钮的事务处理。问题来了:并不是点击所有的按钮都有反应,有反应的按钮主要是其周边存在空白的那个按钮。所以需要判断当前被点击的按钮与空白按钮的距离;只有当两者相邻的时候才能进行icon重置的操作。

此处就涉及到两个对象,其中一个负责按钮的事务处理;另一个表示当前空白的那个按钮;


事务处理的按钮暂停命名为:buttonClick

 


而空白按钮也作为一个对象hole,其包含的主要方法:


方法1:设置当前空白按钮所在的坐标;


方法2:判断当前hole的位置是否与被点击的按钮相邻;



按照上述方案,拼图游戏基本可以实现。