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.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/3widthbut=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=3jbs[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 blocke.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);}}//按鈕的單擊事件執行的代碼@Overridepublic 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==1if (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,yint x=jbs[i].getX()/widthbut;int y=jbs[i].getY()/heightbut;//判斷,通過下標值,也可以獲取按鈕的坐標 橫坐標 i%ls 縱坐標 i/lsif (i%ls!=x || i/ls!=y ){return false;}}return true;}}
右面版
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/3widthbut=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=3jbs[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 blocke.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);}}//按鈕的單擊事件執行的代碼@Overridepublic 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==1if (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,yint x=jbs[i].getX()/widthbut;int y=jbs[i].getY()/heightbut;//判斷,通過下標值,也可以獲取按鈕的坐標 橫坐標 i%ls 縱坐標 i/lsif (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() {@Overridepublic void actionPerformed(ActionEvent e) {//啟動線程//如果線程沒有啟動,則調用start方法啟動if(!th.isAlive()) th.start();startTime=System.currentTimeMillis();rp.times=0;rp.randomOrder();}});//結束游戲itemend.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {System.exit(1);}});//選擇難易度itemeasy,itemnormal,itemharditemeasy.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//第一,傳遞2*2到右面板rp.hs=2;rp.ls=2;//第二,調用右面板組件初始化的方法rp.init(url);}});itemnormal.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//第一,傳遞3*3到右面板rp.hs=3;rp.ls=3;//第二,調用右面板組件初始化的方法rp.init(url);}});itemhard.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//第一,傳遞4*4到右面板rp.hs=4;rp.ls=4;//第二,調用右面板組件初始化的方法rp.init(url);}});//游戲記錄顯示itemrecord.addActionListener(new ActionListener() {@Overridepublic 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() {@Overridepublic void actionPerformed(ActionEvent e) {JOptionPane.showMessageDialog(null, "關于拼圖游戲\r\n版本:v2.0\r\n作者:LWL\r\n歡迎進入游戲!");}});//清空記錄itemclear.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {File f = new File("D:\\游戲記錄.dat");if(f.exists()) {f.delete();}}});//實現圖片的更換itemchange.addActionListener(new ActionListener() {@Overridepublic 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 blocke1.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()方法@Overridepublic 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();}}}
}}
效果展示
?