本程序基于下面教材的例程修改
尉哲明,冀素琴,郭珉. 基于Java的综合课程设计. 北京: 清华大学出版社, 2014(12).
设计要求
本游戏的具体要求如下:
(1)在开始游戏之前,用户可以按照默认的游戏等级、默认的图像开始游戏,也可以通过菜单选择游戏的等级和拼图图像。
(2)拼图游戏分为两个等级:普通级别、高级级别。对于普通级别游戏,将一幅图像分成3×3幅图像,将最后一幅图像(图像的右下角)移到游戏界面的副窗口,将其余各幅小图像打乱顺序后放在拼图面板的按钮上,最终目标是通过移动方块恢复原始图像。同理,对于高级级别游戏,是将一幅图像分成4×4幅小图像,后面的要求同上。
(3)用鼠标单击任何与空格子水平或垂直相邻的方块就可以把方块移入到空格子,而当前方块移动之前所在的格子成为空格子。通过不断地移动方块可以将方块一行一行地按图像本来的顺序拼好。
(4)当用户按要求拼好方块后,弹出对话框,提示用户成功的消息,并自动保存用户的成绩。用户单击菜单上相应级别的游戏排行榜,可以看到自己的成绩。
(5)拼图游戏提供三幅图像,用户可以使用这些图像来玩拼图游戏。用户也可以使用菜单选择本地的一幅新图像,然后使用这个新的图像来玩拼图游戏。
(6)游戏的简单规则在游戏界面的“帮助”菜单内的“游戏说明”信息框内已经简单介绍了。游戏前可以先预览看看。
案例练习题目
(1)改进程序。对本案例提供的拼图游戏测试后,可以发现在“开始新游戏”时,玩家登录窗口无法实现“玩家姓名不能为空”这样的功能,且如果单击登录窗口的“取消”按钮时,“取消”按钮失效,程序仍将开始一个新游戏。本题希望读者能修改程序,避免出现这样的状况。
(2)在练习(1)的基础上改进程序。要求实现如果玩家姓名重复,则在保存玩家成绩时,选择玩家历史记录中的最高成绩存入文件,在排行榜中显示玩家的最好成绩。
(3)补全程序。实现本案例程序中ControlGamePanel中的“保存游戏”和“提取游戏”的功能。(备注:可以使用Java I/O流的知识来保存游戏状态和恢复游戏状态。需要注意的是,Image类不可以序列化,不能直接用ObjectOutputStream和ObjectInputStream来写入和读出图像)
上面的三个要求都在ControlGamePanel中更改,其余的七个类不变。
//13
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collections;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class ControlGamePanel extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
GameWindow gameWin;
JButton buttonStart,buttonPreview,buttonSave,buttonDraw,buttonExit;
PuzzlePanel puzzlePanel;
int row,column;
File gradeFile;
ArrayList<Image> imageList;
Cell[][] cells;
Cell lastCell,noImageCell;
JPanel imagePanel;
Image[][] cellImages;
Image[][] imagesState;
JLabel playerName,usedStep;
int step;
JMenu menuImage;
JMenuItem oneGradeItem,twoGradeItem;
public ControlGamePanel(GameWindow gameWin)
{
this.gameWin =gameWin;
buttonStart=new JButton("开始新游戏");
buttonPreview=new JButton("预览全图");
buttonSave=new JButton("保存游戏");
buttonDraw=new JButton("提取游戏");
buttonExit=new JButton("结束当前游戏");
this.buttonStart .addActionListener(this);
this.buttonPreview .addActionListener(this);
this.buttonSave .addActionListener(this);
this.buttonDraw .addActionListener(this);
this.buttonExit .addActionListener(this);
add(this.buttonStart);
add(this.buttonPreview );
add(this.buttonSave );
add(this.buttonDraw );
add(this.buttonExit );
imagePanel=gameWin.getImagePanel();
usedStep=gameWin.getUsedStep();
step=Integer.parseInt(usedStep.getText());
menuImage=gameWin.getMenuImage();
oneGradeItem=gameWin.getOneGradeItem();
twoGradeItem=gameWin.getTwoGradeItem();
}
@Override
public void actionPerformed(ActionEvent e)
{
//“开始新游戏”
if(e.getSource()==buttonStart)
{
String name=JOptionPane.showInputDialog(null,"请输入玩家的姓名:","登录",JOptionPane.PLAIN_MESSAGE);
if(name==null)
return;
if(name.equalsIgnoreCase(""))
{
JOptionPane.showMessageDialog(null,"玩家姓名不能为空!");
return;
}
playerName=gameWin.getPlayerName();
playerName.setText(name);
buttonStart.setEnabled(false);
menuImage.setEnabled(false);
oneGradeItem.setEnabled(false);
twoGradeItem.setEnabled(false);
row=gameWin.getRow();
column=gameWin.getColumn();
puzzlePanel=gameWin.getPuzzlePanel();
cells=puzzlePanel.getCells();
cellImages=puzzlePanel.getCellImages();
imageList=puzzlePanel.getImageList();
//在“预览全图”中使用imagesState来恢复拼图面板界面
imagesState=new Image[row][column];
noImageCell=cells[row-1][column-1];
Image cellImagePanel=noImageCell.getButtonImage();//得到右下角按钮上的图片
//在右边的imagePanel面板上显示右下角的图片按钮
lastCell=new Cell(cellImagePanel);
lastCell.setPreferredSize(new Dimension(Cell.WIDTH ,Cell.HEIGHT ));
imagePanel.removeAll();
imagePanel.add(lastCell);
imagePanel.validate();
noImageCell.setButtonImage(null);
noImageCell.updateUI();
//打乱图片顺序
Collections.shuffle(imageList);
//根据打乱的图片顺序,重新刷新单元格的背景,右下角单元格无图案
MoveListener l=new MoveListener();
//给除了右下角的所有按钮重新显示打乱的图片,并给它们注册ActionListener监视器
int k=0;
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++)
{
if(i==row-1&&j==column-1)
{
break;
}
cells[i][j].setButtonImage(imageList.get(k));
cells[i][j].repaint();
cells[i][j].updateUI();//重新设置其外观值,让其有边框,方便区别不同的单元格,方便拼图
cells[i][j].addMouseListener(l);
k++;
}
}// end of for(int i=0;i<row;i++)
//给右下角无图像的按钮注册动作监视器
noImageCell.addMouseListener(l);
puzzlePanel.validate();
}
//“预览全图”
else if(e.getSource()==buttonPreview)
{
//在游戏界面展示(预览),原图(完整的)
if(e.getActionCommand().equals("预览全图"))
{
//获取预览全图以前游戏的拼图完成情况,将图片顺序地排到二维数组imagesState中
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++)
{
imagesState[i][j]=cells[i][j].getButtonImage();
}
}// end of for(int i=0;i<row;i++)
//预览原图
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++)
{
cells[i][j].setButtonImage(cellImages[i][j]);
cells[i][j].repaint();
cells[i][j].setBorder(null);
}
}// end of for(int i=0;i<row;i++)
buttonPreview.setText("返回");
}
else if(e.getActionCommand().equals("返回"))
{
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++)
{
cells[i][j].setButtonImage(imagesState[i][j]);
cells[i][j].repaint();
cells[i][j].updateUI();
}
}// end of for(int i=0;i<row;i++)
buttonPreview.setText("预览全图");
}
}// end of else-if(e.getSource()==buttonPreview)
//“保存游戏”
else if(e.getSource()==buttonSave)
{
/*
//保存当前游戏状态,供下次接着玩
//保存当前游戏的拼图完成状况,将图片顺序地排到二维数组imageIcons中;Image不能序列化;ImageIcon可以序列化
*/
ImageIcon[][] icons=new ImageIcon[row][column];
Point p=noImageCell.getButtonPoint();
int x=(int)p.getX();
int y=(int)p.getY();
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++)
{
if(i==x&&j==y)
{
icons[i][j]=new ImageIcon();
continue;
}
icons[i][j]=new ImageIcon(cells[i][j].getButtonImage());
}
}// end of for(int i=0;i<row;i++)
try
{
FileOutputStream fos=new FileOutputStream("saveGame.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeUTF(playerName.getText());
oos.writeInt(step);
//noImageCell的Point
oos.writeInt(x);
oos.writeInt(y);
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++)
{
oos.writeObject(icons[i][j]);
oos.flush();
}
}// end of for(int i=0;i<row;i++)
ImageIcon lastImageIcon=new ImageIcon(lastCell.getButtonImage());
oos.writeObject(lastImageIcon);
oos.flush();
///
boolean boo=false;
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++,boo=false)
{
for(int k=0;k<row;k++)
{
if(boo)
break;
for(int l=0;l<column;l++)
{
if(cells[i][j].getButtonImage()==cellImages[k][l])
{
oos.writeInt(k);
oos.writeInt(l);
oos.flush();
boo=true;
break;
}
}// end of for(int l=0;l<column;l++)
}
}// end of for(int j=0;j<column;j++,boo=false)
}// end of for(int i=0;i<row;i++)
///
oos.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}// end of else-if(e.getSource()==buttonSave)
//“提取游戏”
else if(e.getSource()==buttonDraw)
{
//提取上次未完成的游戏界面,接着玩
//cells
ImageIcon[][] imageHistory=new ImageIcon[row][column];
cellImages=new Image[row][column];
try
{
FileInputStream fis=new FileInputStream("saveGame.txt");
ObjectInputStream ois=new ObjectInputStream(fis);
String name=ois.readUTF();
step=ois.readInt();
int x=ois.readInt();
int y=ois.readInt();
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++)
{
imageHistory[i][j]=(ImageIcon)ois.readObject();
}
}// end of for(int i=0;i<row;i++)
ImageIcon lastImageIcon=(ImageIcon)ois.readObject();
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++)
{
if(i==x&&j==y)
break;
int a=ois.readInt();
int b=ois.readInt();
cellImages[a][b]=imageHistory[i][j].getImage();
}
}
puzzlePanel=gameWin.getPuzzlePanel();
puzzlePanel.setCellImages(cellImages);
//恢复游戏到中断界面
playerName.setText(name);
usedStep.setText(String.valueOf(step));
cells=puzzlePanel.getCells();
for(int i=0;i<row;i++)
{
for(int j=0;j<column;j++)
{
cells[i][j].setButtonImage(imageHistory[i][j].getImage());
cells[i][j].repaint();
cells[i][j].updateUI();
}
}// end of for(int i=0;i<row;i++)
puzzlePanel.setCells(cells);
noImageCell=cells[x][y];
noImageCell.updateUI();
puzzlePanel.validate();
Cell cell=new Cell(lastImageIcon.getImage());
cell.setPreferredSize(new Dimension(Cell.WIDTH ,Cell.HEIGHT ));
imagePanel.removeAll();
imagePanel.add(cell);
imagePanel.validate();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}// end of else-if(e.getSource()==buttonDraw)
//“结束当前游戏”
else if(e.getSource()==buttonExit)
{
//指不愿意继续玩,想退出游戏,开始新游戏;或者是成功完成游戏,想继续玩
//重新初始化游戏面板,会显示初始默认图像,即回到游戏的最初界面
puzzlePanel.initPanel();
imagePanel.removeAll();
this.buttonStart.setEnabled(true);
//设置游戏界面回到初始界面
usedStep.setText("0");
playerName.setText(" ");
step=0;
menuImage.setEnabled(true);
oneGradeItem.setEnabled(true);
twoGradeItem.setEnabled(true);
//step=Integer.parseInt(usedStep.getText());
}
}
//处理图片按钮移动的类,内部类
public class MoveListener implements MouseListener
{
public MoveListener()
{ }
@Override
public void mouseClicked(MouseEvent e)
{ }
@Override
public void mouseEntered(MouseEvent e)
{ }
@Override
public void mouseExited(MouseEvent e)
{ }
//交换相邻单元格的图像
@Override
public void mousePressed(MouseEvent e)
{
Cell currentCell=(Cell)e.getSource();
//如果点击的图片与空单元相邻——水平相邻或垂直相邻,则交换二者图像
if(currentCell.isNeighboringCell(noImageCell))
{
Image img=currentCell.getButtonImage();
noImageCell.setButtonImage(img);
noImageCell.requestFocus();
noImageCell=currentCell;
noImageCell.setButtonImage(null);
usedStep.setText(String.valueOf(++step));
}
}// end of public-void-mousePressed(MouseEvent e)
//检测是否成功完成游戏
@Override
public void mouseReleased(MouseEvent e)
{
if(puzzlePanel.isFinished())
{
gradeFile=gameWin.getGradeFile();
//游戏自动将最后一张图补全;
cells[row-1][column-1].setButtonImage(lastCell.getButtonImage());
puzzlePanel.requestFocus();
//弹出消息窗体,恭喜玩家;并将结果写入排行榜
///显示消息对话框
JOptionPane.showMessageDialog(null,"恭喜你完成了拼图!玩家成绩将自动保存到排行榜中");
///将成绩写入排行榜,即保存玩家成绩
try
{
RandomAccessFile out=new RandomAccessFile(gradeFile,"rw");
long length=gradeFile.length();
long filePoint=0;
boolean b=false;//检测是否有重复的玩家姓名
while(filePoint<length)
{
String name=out.readUTF();
filePoint=out.getFilePointer();
int s=out.readInt();
//如果玩家姓名重复,选择玩家历史记录中的最高成绩存入文件
if( name.equals(playerName.getText()) && step<s )
{
b=true;
out.seek(filePoint);
out.writeInt(step);
break;
}
filePoint=out.getFilePointer();
}
if(b==false)
{
out.seek(length);
out.writeUTF(playerName.getText());
out.writeInt(step);
}
out.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}// end of if(puzzlePanel.isFinished())
}// end of public-void-mouseReleased(MouseEvent e)
}// end of public-class-MoveListener-implements-MouseListener
}