大家好,我是陈橘又青,今天用Java编程实现图形化界面的魔板游戏,以下是完整的开发思路以及代码,供各位讨论交流。
目录
1️⃣效果展示
①图像玩法
②数字玩法
③测试界面
2️⃣项目介绍
①项目背景
②功能分析
③设计要求
3️⃣代码展示
①图形界面设计(gui包)
主类:AppWindows类
②用户操作设计(data包)
Block类
HandleImage类
Point类
VerifySuccess类
③游戏视图设计(view包)
HandleMove类
PuzzlePad类
4️⃣代码测试
5️⃣项目结构
6️⃣设计总结
1️⃣效果展示
①图像玩法
初级模式
高级模式
②数字玩法
③测试界面
2️⃣项目介绍
①项目背景
魔板游戏是一款益智游戏,该游戏有两种形式:一种是由若干个有序不等数字组成,而另一种是由图像组成。人们在将其恢复至最原始的序列而形成完整的排列时,会充分考验玩家的智商,是人们休闲娱乐时可供选择的消遣工具。
魔板游戏分为两个级别,用户可以根据自己的水平来选择“初级”或者“高级”,更具灵活性,并且更贴近用户的需求。对于“初级”级别,魔板由 3*3个格子组成,对于“高级”级别,魔板由 4*4 个格子组成。
②功能分析
该游戏需要实现以下几个功能:
(1) 用户以“数字”或“图像”的形式来玩魔板游戏。
(2) 用户可以根据个人需要来选择魔板游戏的级别:“初级”或者“高级”。
③设计要求
①魔板由3*3或4*4个格子组成。对于3*3魔板:在前八个格子里随机放置8个编号1~8的方块,最后一个格子是未放置方块的空格子,对于4*4的魔板:在前15个格子里随机放置15个编号为 1~15的方块,最后一个格子是未放置方块的空格子。
②用鼠标单击任何与空格子水平或垂直相邻的方块可以把该方块移入空格子,而当前方块移动之前所在的格子成为空格子。通过不断地移动方块可以将方块一行一行地按数字顺序排好。
③魔板游戏也可用图像来代替数字。如:对于3*3的魔板,将一幅图像分成3*3幅小图像,除去最后一幅小图像(图像的右下角)将其余各幅小图像打乱顺序放在魔板的方块上,最终目标是通过移动方块恢复原始图像(不包括图像的右下角)。
④当用户按要求排列好方块后,程序弹出对话框,提示用户成功的消息。
⑤魔板游戏分为两个级别:用户可以通过界面上提供的菜单来选择“初级”或者“高级”两个级别。
⑥“魔板游戏”提供一幅默认图像,用户可以使用该图像来玩魔板游戏。用户也可以使界面提供的菜单选择一幅新图像来玩该游戏。
3️⃣代码展示
①图形界面设计(gui包)
主类:AppWindows类
AppWindow类负责创建游戏的主窗口,该类含有main方法,程序从该类开始执行。
package ch7.gui;import java.awt.*;import java.awt.event.*;import javax.swing.*;import java.io.*;import javax.swing.filechooser.*;import ch7.view.PuzzlePad;public class AppWindow extends JFrame implements ActionListener{PuzzlePad puzzlePad;JMenuBar bar;JMenu gradeMenu,choiceImage;JMenuItem oneGrade,twoGrade,newImage,defaultImage;JRadioButton digitPlay,imagePlay;ButtonGroup group=null;JButton startButton;Image image;Toolkit tool;public AppWindow(){tool=getToolkit();bar=new JMenuBar();gradeMenu=new JMenu("选择级别");choiceImage=new JMenu("选择图像");oneGrade=new JMenuItem("初级");twoGrade=new JMenuItem("高级");newImage=new JMenuItem("选择一幅新图像");defaultImage=new JMenuItem("使用默认图像"); gradeMenu.add(oneGrade);gradeMenu.add(twoGrade);choiceImage.add(newImage);choiceImage.add(defaultImage);bar.add(gradeMenu);bar.add(choiceImage);setJMenuBar(bar);oneGrade.addActionListener(this);twoGrade.addActionListener(this);newImage.addActionListener(this);defaultImage.addActionListener(this);startButton=new JButton("开始");startButton.addActionListener(this);group=new ButtonGroup();digitPlay=new JRadioButton("数字玩法",true);imagePlay=new JRadioButton("图像玩法",false);group.add(digitPlay);group.add(imagePlay);puzzlePad=new PuzzlePad();puzzlePad.setGrade(1);puzzlePad.setIsDigitPlay();add(puzzlePad,BorderLayout.CENTER);JPanel pNorth=new JPanel();pNorth.add(digitPlay);pNorth.add(imagePlay); pNorth.add(startButton);pNorth.add(new JLabel("如果图像不能立刻显示,请再单击一次按扭"));add(pNorth,BorderLayout.NORTH);add(puzzlePad.getHandleMove(),BorderLayout.SOUTH);validate();setVisible(true);setBounds(100,50,550,380);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);try{image=tool.createImage(new File("拼图图像/default.jpg").toURI().toURL());puzzlePad.setImage(image);}catch(Exception exp){} }public void actionPerformed(ActionEvent e){if(e.getSource()==startButton){if(digitPlay.isSelected()){puzzlePad.setIsDigitPlay();}else if(imagePlay.isSelected()){puzzlePad.setImage(image);puzzlePad.setIsImagePlay();}} else if(e.getSource()==oneGrade){puzzlePad.setGrade(1);}else if(e.getSource()==twoGrade){puzzlePad.setGrade(2);}else if(e.getSource()==newImage){FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG & GIF Images", "jpg", "gif");JFileChooser chooser=new JFileChooser();chooser.setFileFilter(filter); int state=chooser.showOpenDialog(null);File file=chooser.getSelectedFile();if(file!=null&&state==JFileChooser.APPROVE_OPTION){try{image=tool.createImage(file.toURI().toURL());puzzlePad.setImage(image);}catch(Exception exp){} }}else if(e.getSource()==defaultImage){try{image=tool.createImage(new File("拼图图像/default.jpg").toURI().toURL());puzzlePad.setImage(image);}catch(Exception exp){} }}public static void main(String args[]){new AppWindow();} }
②用户操作设计(data包)
Block类
Block类是 JTextField的一个子类创建的对象是 PuzzlePad类的重要成员之一,用来表示“魔板”中的“方块”
package ch7.data;import javax.swing.*;import java.awt.*;public class Block extends JTextField{Point point; //方块所在点Object object;//方块上的图像Point [][] allPoint;//全部点位置int index_i,index_j ; //点的索引public Block(){setEditable(false);setHorizontalAlignment(JTextField.CENTER);setFont(new Font("Arial",Font.BOLD,16));setForeground(Color.blue);} public void setAtPoint(Point p){point=p;}public void setAllPoint(Point [][] point){allPoint = point;}public Point getAtPoint(){return point;}public void setObject(Object object){this.object=object;if(object instanceof Integer){Integer number=(Integer)object;setText(""+number.intValue());}else if(object instanceof Image){repaint();}}public Object getObject(){return object;}public void paintComponent(Graphics g){super.paintComponent(g); int w=getBounds().width;int h=getBounds().height;try{ g.drawImage((Image)object,0,0,w,h,this);}catch(Exception exp){}}public boolean move(){int m = -1,n=-1;boolean successMove = false;Point pStart = getAtPoint();findIndex(pStart,allPoint); // 见后面的findIndex(Point p,Point[][] allPoint)方法for(int i = 0;i<allPoint.length;i++){ //得到空盒子的位置索引m,nfor(int j = 0;j<allPoint[i].length;j++){if(!allPoint[i][j].isHaveBlock()){m = i;n = j;}}}if (Math.abs(index_i-m)+Math.abs(index_j-n) == 1){this.setAtPoint(allPoint[m][n]); //当前方块到达allPoint[m][n]点(空盒处)successMove = true;allPoint[m][n].setBlock(this);allPoint[m][n].setHaveBlock(true);pStart.setHaveBlock(false); //设置该点没有方块pStart.setBlock(null); //设置该点上的方块是null(设置为空盒子)}return successMove ; }private void findIndex(Point p,Point[][] allPoint){ //寻找p在allPoint中的索引位置for(int i = 0;i<allPoint.length;i++){for(int j = 0;j<allPoint[i].length;j++){if(p == allPoint[i][j]){index_i = i;index_j = j;break;}}}}}
HandleImage类
HandleImage类所创建的对象负责将一幅图像分成若干个小图像。
package ch7.data;import java.awt.*;import javax.swing.*;import java.awt.image.*;public class HandleImage extends JComponent{int imageWidth,imageHeight;Toolkit tool; public HandleImage(){tool=getToolkit(); }public Image [] getImages(Image image,int rows,int colums){Image [] blockImage=new Image[rows*colums];try{imageWidth=image.getWidth(this);imageHeight=image.getHeight(this);int w=imageWidth/colums;int h=imageHeight/rows;int k=0; //把图像分成k份,即rows*colums份PixelGrabber pg=null; ImageProducer ip=null; for(int i=0;i<rows;i++){for(int j=0;j<colums;j++){int pixels[]= new int[w*h];//存放第k份图像的像素的数组 //将图像image中(j*w,i*h,w,h)矩形区域的像素放到数组pixels的第0行至第w行中:pg=new PixelGrabber(image,j*w,i*h,w,h,pixels,0,w); pg.grabPixels();ip=new MemoryImageSource(w,h,pixels,0,w);//用数组pixels第0行至w行像素做图像源blockImage[k]=tool.createImage(ip); //得到宽是w高是h的矩形Image对象k++; }}}catch(Exception ee){} return blockImage;}}
Point类
Point类负责创建确定位置的对象,使用 Point对象可以确定 Block对象在PuzzlePad对象中的位置,即确定“方块”在“魔板”中的位置。
package ch7.data;public class Point{int x,y; //在坐标系中的x-坐标和y-坐标boolean haveBlock; //点上是否有方块Block block=null; //点上的方块public Point(){} public Point(int x,int y){this.x=x;this.y=y;}public boolean isHaveBlock(){return haveBlock;}public void setHaveBlock(boolean boo){haveBlock=boo;}public int getX(){return x;}public int getY(){return y;}public void setBlock(Block block){this.block=block; }public Block getBlock(){return block;}}
VerifySuccess类
VerifySuccess类所创建的对象负责验证用户是否按要求成功排列魔板中的方块。
package ch7.data;public class VerifySuccess{Point [][] point;Object [] object;public void setPoint(Point [][] point){this.point=point;}public void setObject(Object [] object){this.object=object;}public boolean isSuccess(){if(point[point.length-1][point[0].length-1].getBlock()!=null) //如果右下角有方块return false;boolean boo=true;int k=0;for(int i=0;i<point.length;i++){if(i<point.length-1){for(int j=0;j<point[i].length;j++){ if(!(point[i][j].getBlock().getObject()==object[k])){boo=false;break;}k++;} }else{for(int j=0;j<point[i].length-1;j++){ if(!(point[i][j].getBlock().getObject()==object[k])){boo=false;break;}k++;} } } return boo;} }
③游戏视图设计(view包)
HandleMove类
HandleMove类所创建的对象负责处理鼠标事件。
package ch7.view;import java.awt.event.*;import java.awt.*;import javax.swing.*;import ch7.data.Point;import ch7.data.Block;import ch7.data.HandleImage;import ch7.data.VerifySuccess;public class HandleMove extends JPanel implements MouseListener,ActionListener { Point [][] point;int spendTime=0;javax.swing.Timer recordTime;JTextField showTime;VerifySuccess verify;HandleMove(){recordTime=new javax.swing.Timer(1000,this);showTime = new JTextField(16);showTime.setEditable(false);showTime.setHorizontalAlignment(JTextField.CENTER);showTime.setFont(new Font("楷体_GB2312",Font.BOLD,16));JLabel hitMess=new JLabel("用鼠标单击方块",JLabel.CENTER);hitMess.setFont(new Font("楷体_GB2312",Font.BOLD,18));add(hitMess) ;add(showTime);setBackground(Color.cyan);} public void setPoint(Point [][] p){point=p;}public void initSpendTime(){recordTime.stop();spendTime=0;showTime.setText(null);}public void setVerifySuccess(VerifySuccess verify){this.verify=verify;}public void mousePressed(MouseEvent e){recordTime.start();Block block=null;block=(Block)e.getSource();Point startPoint=block.getAtPoint();int w=block.getBounds().width;int h=block.getBounds().height;if(block.move()){Point pEnd = block.getAtPoint();//得到方块移动后所在点int x = pEnd.getX();int y = pEnd.getY();block.setLocation(x,y);block.setAtPoint(pEnd); pEnd.setBlock(block);pEnd.setHaveBlock(true);startPoint.setHaveBlock(false);}} public void actionPerformed(ActionEvent e){spendTime++;showTime.setText("您的用时:"+spendTime+"秒");} public void mouseReleased(MouseEvent e){if(verify.isSuccess()){recordTime.stop();JOptionPane.showMessageDialog(this,"您成功了!","消息框",JOptionPane.INFORMATION_MESSAGE );}}public void mouseEntered(MouseEvent e){}public void mouseExited(MouseEvent e){}public void mouseClicked(MouseEvent e){}}
PuzzlePad类
PuzzlePad类创建的对象是 PuzzleGame类最重要的成员之一,代表“魔板”。
package ch7.view;import javax.swing.*;import java.util.*;import java.awt.*;import ch7.data.Point;import ch7.data.Block;import ch7.data.HandleImage;import ch7.data.VerifySuccess;public class PuzzlePad extends JPanel{Point [][] point;Block [][] block;int distance=56,grade,m=3,n=3;HandleMove handleMove;HandleImage handleImage; VerifySuccess verifySuccess; Image image;boolean isDigitPlay; public PuzzlePad(){setBackground(Color.gray);setLayout(null);handleMove=new HandleMove();handleMove.initSpendTime();handleImage=new HandleImage();verifySuccess=new VerifySuccess();handleMove.setVerifySuccess(verifySuccess);}public HandleMove getHandleMove(){return handleMove;}public void setImage(Image image){this.image=image;}public void setGrade(int grade){this.grade=grade;if(grade==1){m=3;n=3;} else if(grade==2){m=4;n=4;}}public int getGrade(){return grade;}private void needInit(){handleMove.initSpendTime();removeAll();point=new Point[m][n];block=new Block[m][n];int Hspace=distance,Vspace=distance;for(int i=0;i<m;i++){for(int j=0;j<n;j++){point[i][j]=new Point(Hspace,Vspace);Hspace=Hspace+distance;}Hspace=distance;Vspace=Vspace+distance;} handleMove.setPoint(point);verifySuccess.setPoint(point);handleMove.setVerifySuccess(verifySuccess); int k=0;for(int i=0;i<m;i++){if(i<m-1)for(int j=0;j<n;j++){block[i][j]=new Block();block[i][j].setAllPoint(point);block[i][j].addMouseListener(handleMove);k++; }elsefor(int j=0;j<n-1;j++){block[i][j]=new Block();block[i][j].setAllPoint(point);block[i][j].addMouseListener(handleMove);k++;}} for(int i=0;i<m;i++){if(i<m-1)for(int j=0;j<n;j++){add(block[i][j]);block[i][j].setSize(distance,distance);block[i][j].setLocation(point[i][j].getX(),point[i][j].getY());block[i][j].setAtPoint(point[i][j]);point[i][j].setBlock(block[i][j]);point[i][j].setHaveBlock(true);}elsefor(int j=0;j<n-1;j++){add(block[i][j]);block[i][j].setSize(distance,distance);block[i][j].setLocation(point[i][j].getX(),point[i][j].getY());block[i][j].setAtPoint(point[i][j]);point[i][j].setBlock(block[i][j]);point[i][j].setHaveBlock(true);}}}public void setIsDigitPlay(){needInit();isDigitPlay=true;ArrayList<Integer> numberList=new ArrayList<Integer>();for(int k=0;k<m*n-1;k++){numberList.add(k+1);}Object []object=numberList.toArray();verifySuccess.setObject(object);Collections.shuffle(numberList); //随机排列数字 int k=0;for(int i=0;i<m;i++){if(i<m-1)for(int j=0;j<n;j++){block[i][j].setObject(numberList.get(k));k++; }elsefor(int j=0;j<n-1;j++){block[i][j].setObject(numberList.get(k));k++;}} repaint(); }public void setIsImagePlay(){needInit(); isDigitPlay=false;ArrayList<Image> imageList=new ArrayList<Image>();Image [] blockImage=handleImage.getImages(image,m,n);for(int k=0;k<blockImage.length-1;k++){imageList.add(blockImage[k]);}Object []object=imageList.toArray();verifySuccess.setObject(object);Collections.shuffle(imageList); //随机排列图像int k=0;for(int i=0;i<m;i++){if(i<m-1)for(int j=0;j<n;j++){block[i][j].setObject(imageList.get(k));block[i][j].repaint();block[i][j].setBorder(null); k++; }elsefor(int j=0;j<n-1;j++){block[i][j].setObject(imageList.get(k));block[i][j].repaint();block[i][j].setBorder(null); k++;}} repaint(); }public void paintComponent(Graphics g){super.paintComponent(g);if(isDigitPlay==false) try{ g.drawImage(image,20+distance*(m+1),point[0][0].getY(),distance*m,distance*n,this);}catch(Exception exp){}}}
4️⃣代码测试
这里我们创建test包,实现AppTest类来进行代码的测试,代码如下:
package ch7.test;import ch7.data.*;public class AppTest {public static void main(String [] args) {Point [][] point = new Point[3][3]; //3行3列的魔板中的点for(int i=0;i<point.length;i++) {for(int j = 0;j<point[i].length;j++)point[i][j] = new Point();} Block block[][] = new Block[3][3];for(int i=0;i<block.length;i++) { //3行3列的魔板中的方块for(int j = 0;j<block[i].length;j++) {block[i][j] = new Block();block[i][j].setAllPoint(point);block[i][j].setAtPoint(point[i][j]);point[i][j].setHaveBlock(true);point[i][j].setBlock(block[i][j]);}}point[2][2].setHaveBlock(false); //右下角设置没有方块point[2][2].setBlock(null); //右下角设置为null方块VerifySuccess verifySuccess = new VerifySuccess();//负责判断是否成功Integer [] number = {1,2,3,4,5,6,7,8};verifySuccess.setPoint(point);verifySuccess.setObject(number);block[0][0].setObject(number[0]); block[0][1].setObject(number[1]);block[0][2].setObject(number[2]); block[1][0].setObject(number[3]);block[1][1].setObject(number[7]); block[1][2].setObject(number[4]);block[2][0].setObject(number[6]); block[2][1].setObject(number[5]);intput(point);System.out.println("----------------------------");System.out.println("移动2次:");System.out.println(point[2][1].getBlock().move());System.out.println(point[1][1].getBlock().move());intput(point);System.out.println("成功否:"+verifySuccess.isSuccess());System.out.println("再移动2次:");System.out.println(point[1][2].getBlock().move());System.out.println(point[2][2].getBlock().move());intput(point);System.out.println("成功否:"+verifySuccess.isSuccess());}static void intput(Point [][] point){int k = 0;for(int i=0;i<point.length;i++) {for(int j = 0;j<point[i].length;j++){String s ="";Block bk= point[i][j].getBlock();if(bk!=null){Integer object = (Integer)bk.getObject(); s =object.toString();}elses ="#";//表示没有方块System.out.printf("%5s",s);}System.out.println();} }}
5️⃣项目结构
6️⃣设计总结
本次项目设计是通过 Java语言编制一个魔板游戏,它是一款经典的智力游戏。而Java语言是当今较为流行的网络编程语言,它具有面向对象、跨平台、分布应用等特点。这次设计,还有利于加深对 Java课程的进一步了解,也可以巩固所学 Java语言基本知识,增进 Java语言编辑基本功,掌握 JDK、Editplus、Idea、JCreator等开发工具的运用,拓宽常用类库的应用。使学生通过该教学环节与手段,把所学课程及相关知识加以融会贯通,全面掌握 Java语言的编程思想及面向对象程序设计的方法。