1、了解拼图游戏基本功能:
拼图游戏内容由若干小图像块组成的,通过鼠标点击图像块上下左右移动,完成图像的拼凑。
2、拼图游戏交互界面设计与开发:
通过创建窗体类、菜单、中间面板和左右面板完成设计拼图的交互界面 ,实现拼图游戏的基本功能。
3、图片的加载与分割:
使用Image类实现图片的缩放,ImageIO类实现图片的读写加载,通过接口类Icon,BufferedImage类获取BufferedImage类的对象实现图片分割。
4、图片随机打乱和交换:
产生随机数 Random rand=new Random();
rand.nextInt(hs*ls)------[0,8]
具体操作:生成两个随机数表示数组下标,互换两个数组元素的位置,按钮的方法getX和getY可以获取按钮的坐标,利用按钮的单击事件的处理ActionListener可以使其图片交换。
5、判赢:
当用户移动按钮后进行判断,代码写在监听器的actionPerformed方法中,判断拼图是否成功,主要取决于每一个按钮通过索引下标获取的位置值,与当前按钮的位置值是否相同。
6、计时和计数功能的实现:
计时功能的实现主要是线程的设计,线程的定义方法:第一:继承Thread类,第二:实现Runnable接口,创建带实现接口的子类对象的Thread对象,MainJFrame实现Runnable接口,重写run方法;而计数则在主窗体中通过rp.times实现对变量的使用来计数。
7、游戏记录的保存:
当用户拼图成功后,记录当前信息到文件中,FileWriter追加写信息,FileReader完成读取数据。
- import java.awt.Color;
- import java.awt.Font;
- import java.awt.GridLayout;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
-
- import javax.swing.JButton;
- import javax.swing.JFrame;
- import javax.swing.JLabel;
- import javax.swing.JOptionPane;
- import javax.swing.JPasswordField;
- import javax.swing.JTextField;
-
- public class LoginPintu extends JFrame{
- JLabel jl1,jl2,jl3,jl4;
- JTextField jtf;//文本框
- JPasswordField jpf;//密码
- JButton jb1,jb2;
- public LoginPintu() {
- this.setTitle("拼图游戏");
- setBounds(400,350,500,400);
- //设置窗体为流式布局
- setLayout(new GridLayout(20,1));
- //空布局
- setLayout(null);
- init();
- setVisible(true);
- setDefaultCloseOperation(EXIT_ON_CLOSE);
- jb1.addActionListener(new ActionListener(){
- public void actionPerformed(ActionEvent e) {
- if(jtf.getText().trim().equals("admin")&&
- new String(jpf.getPassword()).trim().equals("123"))
- {JOptionPane.showMessageDialog(null, "欢迎进入游戏!");
- new MainJFrame();}
- else if(jtf.getText().trim().length()==0||
- new String(jpf.getPassword()).trim().length()==0)
- {JOptionPane.showMessageDialog(null, "用户名或密码不能为空!");}
- else {JOptionPane.showMessageDialog(null, "用户名或密码错误!");}
- }
- });
- jb2.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- // System.exit(0);
- //获取事件源对象
- JButton jb=(JButton)e.getSource();
- jtf.setText(jb.getText());
- }
- });
-
- }
- public void init() {
- jl1=new JLabel("拼图游戏登录窗口");
- jl2=new JLabel("用户名:");
- jl3=new JLabel("密码:");
- jtf=new JTextField(10);
- jpf=new JPasswordField(10);
- jb1=new JButton("登录");
- jb2=new JButton("取消");
- jl1.setBounds(150,30,200,60);
- jl2.setBounds(100, 120, 180, 30);
- jtf.setBounds(200, 120, 180, 30);
- jl3.setBounds(100, 180, 180, 30);
- jpf.setBounds(200, 180, 180, 30);
- jb1.setBounds(100, 260, 100, 30);
- jb2.setBounds(220, 260, 100, 30);
- Font font = new Font("楷体",Font.PLAIN,25);
- jl1.setFont(font);
- jl1.setForeground(Color.red);
- add(jl1);
- add(jl2);
- add(jtf);
- add(jl3);
- add(jpf);
- add(jb1);
- add(jb2);
- }
- public static void main(String[] args) {
- new LoginPintu();
- }
- }
左面板部分
- import java.awt.Image;
- import java.net.MalformedURLException;
- import java.net.URL;
-
- import javax.swing.ImageIcon;
- import javax.swing.JLabel;
- import javax.swing.JPanel;
- //左面板类
- public class LeftJPanel extends JPanel {
- JLabel jl;
- int width=700;
- int height=700;
-
- //构造方法
- //标签创建,指定图片,放置到面板中
- public LeftJPanel(){
- //左面板大小
- setSize(width,height);
- jl=new JLabel();
- jl.setSize(width,height);
- //把标签添加到面板中
- this.add(jl);
- }
- public void init(URL url){
-
-
- //绝对路径:访问文件是从盘符开始
- // ImageIcon icon=new ImageIcon("D:\\1picture\\s4.jpg");
- //相对路径:访问路径不是从盘符开始,可以是\,也可以是一个文件夹
- // ImageIcon icon=new ImageIcon("s4.jpg");//参数是字符串的相对路径,相对于当前项目根目录
- //相对路径下url的获取
-
- // //绝对路径的url的获取
- // URL url=null;
- // try {
- // url = new URL("file:\\D:\\1picture\\5.jpg");
- // } catch (MalformedURLException e) {
- // // TODO Auto-generated catch block
- // e.printStackTrace();
- // }
- ImageIcon icon=new ImageIcon(url);
- //方法一:图片缩放
- // Image image = icon.getImage();
- // Image image2 = image.getScaledInstance(700, 700, 1);
- // ImageIcon icon2 = new ImageIcon(image2);
- // jl.setIcon(icon2);
- //链式编程方式实现图片缩放
- jl.setIcon(new ImageIcon(icon.getImage().getScaledInstance(width, height, 1)));
- //刷新界面
- validate();
- }
- }
右面板部分
- import java.awt.Image;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.awt.image.BufferedImage;
- import java.io.FileInputStream;
- import java.io.FileWriter;
- import java.io.IOException;
- import java.net.URL;
- import java.util.Random;
-
- import javax.imageio.ImageIO;
- import javax.swing.ImageIcon;
- import javax.swing.JButton;
- import javax.swing.JOptionPane;
- import javax.swing.JPanel;
-
- import jdk.jfr.events.FileWriteEvent;
- //右面板实现ActionListener接口,右面板也就成为了监听器
- public class RightJPanel extends JPanel implements ActionListener{
-
- //面板的大小
- int width=700;
- int height=700;
- //定义按钮数组
- JButton[] jbs;
- //设置分割的行列数
- int hs=2,ls=2;
- //按钮的宽度和高度,指定是小图图片缩放的尺寸
- int widthbut,heightbut;
-
- //图片原始高度宽度
- int widthtp,heighttp;
-
- //小图的原始宽度高度
- int widthxt,heightxt;
-
- //实现步数计算的变量
- int times;
-
- //空白按钮
- JButton kb;
- public RightJPanel(){
- //面板布局是空布局
- setLayout(null);
- setSize(width,height);
- //init();
-
- }
- //创建按钮,并放置到右面板
- public void init(URL url) {
- //面板组件初始化前,先清除所有已有的组件
- this.removeAll();
- //创建按钮数组
- jbs=new JButton[hs*ls];
- //为每一个按钮实现初始化
- //计算按钮的宽度和高度
- //面板是700*700,拆分成3*3的9个区域
- //每一块区域的宽度 700/3
- //每一块区域的高度 700/3
- widthbut=width/ls;
- heightbut=height/hs;
-
- BufferedImage buf=null;
- try {
- buf = ImageIO.read(url);
- //获取原图的宽度、高度
- widthtp=buf.getWidth();
- heighttp=buf.getHeight();
- //获取小图的宽度和高度
- widthxt=widthtp/ls;
- heightxt=heighttp/hs;
- //每一块按钮的坐标位置确定
- for(int i=0;i<jbs.length;i++){
- jbs[i]=new JButton();
- jbs[i].setSize(widthbut,heightbut);
- //jbs[i].setText(i+"");
- //添加按钮前要确定坐标位置
- //横坐标 i=0 0 i=1 233 i=2 466
- //i=3 0 i=4 233
- //纵坐标 i=3
- jbs[i].setLocation((i%ls)*widthbut, i/ls*heightbut);
- //jbs[i].setIcon(null);
- //小图的获取
- BufferedImage subimage = buf.getSubimage(i%ls*widthxt, i/ls*heightxt, widthxt, heightxt);
- //小图的缩放
- Image image = subimage.getScaledInstance(widthbut, heightbut, 1);
- //将小图图片放置到按钮上
- jbs[i].setIcon(new ImageIcon(image));
- //添加按钮到右面板
- add(jbs[i]);
- //设置按钮不可用
- jbs[i].setEnabled(false);
- //设置按钮的监听,当按钮被单击,会到右面板中找actionPerformed方法执行
- jbs[i].addActionListener(this);
- }
- jbs[hs*ls-1].setIcon(null);
- kb=jbs[hs*ls-1];
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
-
- }
-
- //打乱按钮在面板中显示的顺序
- public void randomOrder(){
- //创建随机数对象
- Random rand=new Random();
- //打乱多次
- for(int i=0;i<hs*ls;i++){
- //随机索引
- int index1=rand.nextInt(hs*ls);
- int index2=rand.nextInt(hs*ls);
- int x1=jbs[index1].getX();
- int y1=jbs[index1].getY();
- int x2=jbs[index2].getX();
- int y2=jbs[index2].getY();
- jbs[index1].setLocation(x2, y2);
- jbs[index2].setLocation(x1, y1);
- jbs[i].setEnabled(true);
- }
- }
-
- //按钮的单击事件执行的代码
- @Override
- public void actionPerformed(ActionEvent e) {
- // 判断单击按钮和空白按钮是否相邻,如果相邻,则位置互换
- //获取用户单击的按钮 ,通过ActionEvent e的方法gerSource获取事件源
- JButton jb=(JButton)(e.getSource());
- //获取单击按钮和空白按钮的坐标
- int x1=jb.getX();
- int y1=jb.getY();
- int x2=kb.getX();
- int y2=kb.getY();
- //判断是否可以移动
- //Math.abs(x1-x2)/widthbut + Math.abs(y1-y2)/heightbut==1
- if (Math.abs(x1-x2)/widthbut + Math.abs(y1-y2)/heightbut==1){
- jb.setLocation(x2, y2);
- kb.setLocation(x1, y1);
- times++;
- }
- //判断是否拼图成功
- if (isWin()){
- JOptionPane.showMessageDialog(null, "恭喜你,拼图成功");
- //使得按钮不可用
- for(int i=0;i<jbs.length;i++){
- jbs[i].setEnabled(false);
- }
- //提示用户输入名称
- //使用输入对话框
- String name = JOptionPane.showInputDialog("请输入你的姓名:");
- String info = hs+"*"+ls+"拼图记录:"+name+"的步数是:"+times+"\r\n";
- JOptionPane.showMessageDialog(null, hs+"*"+ls+"拼图记录:"+name+"的步数是:"+times+"\r\n");
- try {
- FileWriter fw = new FileWriter("D:\\游戏记录.dat",true);
- fw.write(info);
- fw.close();
- }catch (IOException e1) {
- e1.printStackTrace();
- }
- }
-
- }
-
- //判断是否拼图成功
- public boolean isWin() {
-
- //获取每一个按钮的坐标
- for(int i=0;i<jbs.length;i++){
- //jbs[i].setLocation((i%ls)*widthbut, i/ls*heightbut);由之前坐标设置给出下面的x,y
- int x=jbs[i].getX()/widthbut;
- int y=jbs[i].getY()/heightbut;
- //判断,通过下标值,也可以获取按钮的坐标 横坐标 i%ls 纵坐标 i/ls
- if (i%ls!=x || i/ls!=y ){
- return false;
- }
- }
- return true;
- }
- }
游戏功能部分
- import java.awt.Color;
- import java.awt.GridLayout;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.io.File;
- import java.io.FileReader;
- import java.io.IOException;
- import java.net.MalformedURLException;
- import java.net.URL;
-
- import javax.swing.ButtonGroup;
- import javax.swing.JFileChooser;
- import javax.swing.JFrame;
- import javax.swing.JLabel;
- import javax.swing.JMenu;
- import javax.swing.JMenuBar;
- import javax.swing.JMenuItem;
- import javax.swing.JOptionPane;
- import javax.swing.JPanel;
- import javax.swing.JRadioButtonMenuItem;
- import javax.swing.filechooser.FileNameExtensionFilter;
-
- public class MainJFrame extends JFrame implements Runnable{
-
- //菜单
- //菜单栏
- JMenuBar jmenubar;
- //菜单 菜单、等级、帮助
- JMenu menu,menuclass,menuhelp;
- //菜单项 开始、退出、图片更换、关于游戏、游戏记录、清空记录
- JMenuItem itembegin,itemend,itemchange,itemabout,itemrecord,itemclear;
- //单选菜单项 简单、一般、困难
- JRadioButtonMenuItem itemeasy,itemnormal,itemhard;
- //中间面板
- JPanel jp;
- //左面板
- LeftJPanel lp;
- //右面板
- RightJPanel rp;
- //访问的图片
- URL url;
- //显示计时标签
- JLabel total_time;
- //起止时间
- long startTime,endTime;
- //创建线程对象,实现计时功能
- Thread th;
- //显示步数的标签
- JLabel total_count;
- //构造方法
- public MainJFrame(){
- //标题设置
- setTitle("拼图游戏");
- //窗体大小
- setSize(1440, 780);
- //窗体位置在容器/屏幕的正中间
- setLocationRelativeTo(null);
- //窗体大小不可变
- setResizable(false);
- //实现界面菜单初始化
- //创建一个线程对象
- th=new Thread(this);
- //界面菜单初始化
- menuinit();
- //各面板的初始化
- init();
- setDefaultCloseOperation(EXIT_ON_CLOSE);
- setVisible(true);
- //开始菜单
- itembegin.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- //启动线程
- //如果线程没有启动,则调用start方法启动
- if(!th.isAlive()) th.start();
- startTime=System.currentTimeMillis();
- rp.times=0;
- rp.randomOrder();
- }
- });
- //结束游戏
- itemend.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- System.exit(1);
- }
- });
- //选择难易度itemeasy,itemnormal,itemhard
- itemeasy.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- //第一,传递2*2到右面板
- rp.hs=2;
- rp.ls=2;
- //第二,调用右面板组件初始化的方法
- rp.init(url);
- }
- });
- itemnormal.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- //第一,传递3*3到右面板
- rp.hs=3;
- rp.ls=3;
- //第二,调用右面板组件初始化的方法
- rp.init(url);
- }
- });
- itemhard.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- //第一,传递4*4到右面板
- rp.hs=4;
- rp.ls=4;
- //第二,调用右面板组件初始化的方法
- rp.init(url);
- }
- });
- //游戏记录显示
- itemrecord.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- //info存储要显示的内容
- String info="";
- try {
- //判断文件是否存在
- File f = new File("D:\\游戏记录.dat");
- if(f.exists()) {
- //创建指向***的文件字符输入流对象
- FileReader fr = new FileReader("D:\\游戏记录.dat");
- //读取数据
- char[] chs = new char[1024];
- int len;
- while((len=fr.read(chs))!=-1) {
- //读取的结果放在info中
- info+=new String(chs,0,len);
- }
-
- fr.close();
- //通过消息框显示结果
- JOptionPane.showMessageDialog(null, info);
- }else {
- JOptionPane.showMessageDialog(null, "游戏记录为空!");
- }
- }catch (IOException e1) {
- e1.printStackTrace();
- }
- }
- });
- //关于游戏
- itemabout.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- JOptionPane.showMessageDialog(null, "关于拼图游戏\r\n版本:v2.0\r\n作者:LWL\r\n欢迎进入游戏!");
- }
- });
- //清空记录
- itemclear.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- File f = new File("D:\\游戏记录.dat");
- if(f.exists()) {
- f.delete();
- }
- }
- });
- //实现图片的更换
- itemchange.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- //显示一个打开对话框,选择一个图片文件,将文件转换成url对象,调用左右面板的相应方法
- JFileChooser jfc=new JFileChooser();
- //设置文件的扩展名
- jfc.setFileFilter(new FileNameExtensionFilter("图片格式(jpg|png|gif|jpeg)", "jpg","png","gif","jpeg"));
- //弹出打开对话框
- int sd = jfc.showOpenDialog(MainJFrame.this);
- if (sd==jfc.APPROVE_OPTION)//如果用户选择了打开按钮
- {
- //获取用户选择的文件完整名称
- String file=jfc.getSelectedFile().getAbsolutePath();
- try {
- url=new URL("file:\\"+file);
- //更新两个面板的图片
- lp.init(url);
- rp.init(url);
- } catch (MalformedURLException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- }
- }
- });
- }
-
- public void init() {
- jp=new JPanel();
- //设置中间面板的布局方式
- jp.setLayout(new GridLayout(1,2));
- //提供左右面板的图片
- url=this.getClass().getResource("小狗.jpg");
- //创建左面板
- lp=new LeftJPanel();
- //对标签初始化
- lp.init(url);
- //将左面板添加到中间面板
- jp.add(lp);
- //创建右面板
- rp=new RightJPanel();
- //右面板的按钮初始化
- rp.init(url);
- //将右面板添加到中间面板
- jp.add(rp);
- //将中间面板添加到窗体
- add(jp);
- }
-
- public void menuinit() {
- jmenubar=new JMenuBar();
- menu=new JMenu("菜单");
- menuclass=new JMenu("等级");
- menuhelp=new JMenu("帮助");
- itembegin=new JMenuItem("开始游戏");
- itemend=new JMenuItem("结束游戏");
- itemchange=new JMenuItem("更换图片");
- itemabout=new JMenuItem("关于游戏");
- itemrecord=new JMenuItem("游戏记录");
- itemclear=new JMenuItem("清空记录");
- itemeasy=new JRadioButtonMenuItem("简单");
- itemnormal=new JRadioButtonMenuItem("一般");
- itemhard=new JRadioButtonMenuItem("困难");
- //为单选菜单分组,实现多选一
- ButtonGroup bg=new ButtonGroup();
- bg.add(itemeasy);
- bg.add(itemnormal);
- bg.add(itemhard);
- //添加菜单
- menu.add(itembegin);
- menu.add(itemend);
- menu.add(itemchange);
-
- menuclass.add(itemeasy);
- menuclass.add(itemnormal);
- menuclass.add(itemhard);
-
- menuhelp.add(itemabout);
- menuhelp.add(itemrecord);
- menuhelp.add(itemclear);
-
- jmenubar.add(menu);
- jmenubar.add(menuclass);
- jmenubar.add(menuhelp);
-
- //菜单栏添加到窗体
- this.setJMenuBar(jmenubar);
- itemeasy.setSelected(true);
- //创建一个线程对象
- th=new Thread(this);
- total_time=new JLabel("用时:");
- total_time.setForeground(Color.red);
- jmenubar.add(new JLabel(" "));
- jmenubar.add(total_time);
- total_count=new JLabel("步数:");
- total_count.setForeground(Color.red);
- jmenubar.add(new JLabel(" "));
- jmenubar.add(total_count);
- }
-
- public static void main(String[] args) {
- new MainJFrame();
- }
- //实现计时并定时显示的run()方法
- @Override
- public void run() {
- while(true) {
- endTime=System.currentTimeMillis();
- total_time.setText("用时:"+(endTime-startTime)/1000+"秒");
- total_count.setText("步数:第"+rp.times+"步");
- try {
- Thread.sleep(500);
- }catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }