连连看大家应该都玩过,不多说直接上一个做好的界面截图吧,所有的功能都在上面的,要做的就只是如何去实现它们了。
差不多就是这个样子。先说一下大致的思路吧。首先编写基本的界面:把什么按钮啊,表格啊什么的都画上去。然后就是编写事件处理类,因为操作使用鼠标,所以加上鼠标监听。然后获取点击的坐标,根据坐标得出图片在数组中的位置。接着创建一个类,实现连连看消除的算法。这样就基本上可以开始游戏了。然后实现排行榜按钮和存档按钮的基本功能。最后加上一个线程类,用于处理倒计时。下面的介绍也基于这个顺序。
界面实现:这个其实没什么好说的,把JFrame的知识用上就好了。考虑到图片的闪烁问题,在界面类中重写paint方法,加上双缓冲(双缓冲不懂的,可以自行百度或者看看我写的Java版本2048)。所以就直接贴代码了。
package com.cbs.look; public interface LookConfig { int x=50;//初始x坐标,原来是10 int y=100;//初始y坐标,原来是50 int space=10;//图片间的间隔 int arc=50;//圆角矩形的弧度 int size=60;//图片的大小 int num=9;//图片类型 }
1 package com.cbs.look; 2 3 import java.awt.Color; 4 import java.awt.Font; 5 import java.awt.Graphics; 6 import java.awt.Graphics2D; 7 import java.awt.Image; 8 import java.awt.RenderingHints; 9 10 import javax.swing.ImageIcon; 11 import javax.swing.JButton; 12 import javax.swing.JFrame; 13 import javax.swing.JLabel; 14 import javax.swing.JOptionPane; 15 16 /** 17 * 连连看的主界面类 18 * 19 * @author CBS 20 * 21 */ 22 @SuppressWarnings("serial") 23 public class GameLook extends JFrame implements LookConfig { 24 25 private int[][] array = new int[8][8];// 数组用于保存界面的信息 26 27 JLabel timeJl;// 用于显示剩余时间 28 29 public static void main(String[] args) throws InterruptedException { 30 GameLook g = new GameLook(); 31 g.showUI(); 32 33 } 34 35 /** 36 * 初始化界面 37 * 38 * @throws InterruptedException 39 */ 40 public void showUI() throws InterruptedException { 41 setTitle("连连看"); 42 setSize(700, 800); 43 setDefaultCloseOperation(3); 44 setLocationRelativeTo(null); 45 setResizable(true); 46 setLayout(null); 47 48 // 添加新游戏按钮 49 // ImageIcon start = new ImageIcon("res/start.png"); 50 JButton startJB = new JButton("新游戏"); 51 startJB.setBounds(30, 700, 100, 40); 52 // startJB.setBorderPainted(false);// 设置边框为空 53 startJB.setFocusable(false); 54 // startJB.setContentAreaFilled(false);// 设置内容空 55 this.add(startJB); 56 // 添加排行榜按钮 57 JButton save = new JButton("排行榜"); 58 save.setFocusable(false); 59 save.setBounds(190, 700, 100, 40); 60 this.add(save); 61 62 // 添加存档按钮 63 JButton saveGame = new JButton("存档"); 64 saveGame.setFocusable(false); 65 saveGame.setBounds(320, 700, 100, 40); 66 this.add(saveGame); 67 68 69 // 添加剩余时间 70 JLabel jl = new JLabel("Time:"); 71 jl.setFont(new Font("", Font.BOLD, 20)); 72 jl.setBounds(440, 700, 80, 50); 73 this.add(jl); 74 75 // 显示剩余时间 76 timeJl = new JLabel("90"); 77 timeJl.setFont(new Font("", Font.BOLD, 20)); 78 timeJl.setBounds(520, 700, 80, 50); 79 this.add(timeJl); 80 81 setVisible(true); 82 83 GameListener gl = new GameListener(); 84 gl.setFrame(this); 85 gl.setTimeJl(timeJl); 86 gl.setArray(array); 87 saveGame.addActionListener(gl); 88 startJB.addActionListener(gl); 89 save.addActionListener(gl); 90 91 int i=JOptionPane.showConfirmDialog(this, "是否读取上次的存档", "读档", 92 JOptionPane.YES_NO_OPTION); 93 if(i==1){ 94 JOptionPane.showMessageDialog(this, "请按新游戏开始游戏吧!"); 95 }else{ 96 GameSave2 gs2=new GameSave2(); 97 CunD c=gs2.opean(); 98 if(c!=null){ 99 array=c.getArray(); 100 gl.setArray(array); 101 this.addMouseListener(gl); 102 this.repaint(); 103 TimeOut tt =new TimeOut(timeJl, this, gl); 104 gl.setTt(tt); 105 tt.setSeconds(c.getTime()); 106 tt.start(); 107 }else{ 108 JOptionPane.showMessageDialog(this, "读取失败!"); 109 } 110 } 111 112 113 } 114 115 @Override 116 public void paint(Graphics g) { 117 super.paint(g); 118 buffPaint(g); 119 } 120 121 /** 122 * 使用双缓冲技术解决闪屏问题 123 * 124 * @param g传入的画笔对象 125 */ 126 public void buffPaint(Graphics g) { 127 Image i = createImage(space + (size + space) * array[0].length, space 128 + (size + space) * array.length); 129 Graphics2D g2d = (Graphics2D) i.getGraphics(); 130 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 131 RenderingHints.VALUE_ANTIALIAS_ON); 132 // 绘制背景矩形 133 g2d.setColor(new Color(210, 180, 140)); 134 g2d.fillRoundRect(0, 0, space + (size + space) * array[0].length, space 135 + (size + space) * array.length, arc, arc); 136 // 绘制背景方格 137 g2d.setColor(new Color(245, 245, 220)); 138 for (int r = 0; r < array.length; r++) { 139 for (int c = 0; c < array[r].length; c++) { 140 g2d.fillRect(space + (size + space) * c, space + (size + space) 141 * r, size, size); 142 } 143 } 144 // 绘制图片 145 g2d.setColor(Color.BLUE); 146 g2d.setFont(new Font("宋体", Font.BOLD, 30)); 147 for (int r = 0; r < array.length; r++) { 148 for (int c = 0; c < array[r].length; c++) { 149 if (array[r][c] != 0) { 150 ImageIcon icon = new ImageIcon("res/" + array[r][c] 151 + ".jpg"); 152 Image image = icon.getImage(); 153 g2d.drawImage(image, space + (size + space) * c, space 154 + (size + space) * r, size, size, null); 155 } 156 } 157 } 158 g.drawImage(i, x, y, this); 159 } 160 }
事件处理类:鼠标的事件处理主要负责的是记录两次点击的坐标,然后判断是否能够把两个图片消除,如果可以消除图片把对应的数组位置的数置为0,然后重绘画板,如果不行同样重绘画板消除选框及连线。动作的事件处理主要负责实现不同的按钮的功能。
package com.cbs.look; import java.awt.BasicStroke; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.List; import java.util.Random; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.plaf.FontUIResource; /** * 事件处理类 * @author CBS */ public class GameListener extends MouseAdapter implements LookConfig, ActionListener { // 用于控制坐标的获取 private boolean flag = true; private int r1, c1, r2, c2;// 对应数组的下标位置 private int x1, y1, x2, y2;// 鼠标点击的坐标 private int array[][];// 保存数组 private JFrame frame;// 用于获取窗体对象,调用Repaint方法 private Graphics2D g;// 画笔对象 JLabel timeJl;// 用于显示剩余时间 TimeOut tt ;// 倒计时线程类 private int x;// 保存画框的顶点x坐标 private int y;// 保存画框的顶点y坐标 public TimeOut getTt() { return tt; } public void setTt(TimeOut tt) { this.tt = tt; } public void setTimeJl(JLabel timeJl) { this.timeJl = timeJl; } public void setFrame(JFrame frame) { this.frame = frame; g = (Graphics2D) frame.getGraphics(); } public void setArray(int[][] array) { this.array = array; } @Override public void mousePressed(MouseEvent e) { // 获取坐标 if (flag) { x1 = e.getX() - 40; y1 = e.getY() - 50; flag = false; if (y1 / (size + space) - 1 >= array.length) r1 = array.length - 1; else if (y1 / (size + space) - 1 < 0) r1 = 0; else r1 = y1 / (size + space) - 1; if (x1 / (size + space) >= array[0].length) c1 = array[0].length - 1; else c1 = x1 / (size + space); g.setColor(Color.RED); g.setStroke(new BasicStroke(5)); x = space + space + c1 * (size + space) + 40; y = size + r1 * (size + space) + 50; g.drawRect(x, y, size, size); } else { x2 = e.getX() - 40; y2 = e.getY() - 50; flag = true; if (y2 / (size + space) - 1 >= array.length) r2 = array.length - 1; else if (y1 / (size + space) - 1 < 0) r1 = 0; else r2 = y2 / (size + space) - 1; if (x2 / (size + space) >= array[0].length) c2 = array[0].length - 1; else c2 = x2 / (size + space); g.setColor(Color.RED); g.setStroke(new BasicStroke(4)); x = space + space + c2 * (size + space) + 40; y = size + r2 * (size + space) + 50; g.drawRect(x, y, size, size); } GameUtil gu = new GameUtil(this.frame); if (array[r1][c1] == array[r2][c2] && flag && !(r1 == r2 && c2 == c1) && (array[r1][c1] != 0 || array[r2][c2] != 0)) { if (gu.wuZhe(r1, c1, r2, c2, array)) { array[r1][c1] = 0; array[r2][c2] = 0; g.setColor(Color.PINK); g.drawLine(2 * space + size / 2 + c2 * (size + space) + 40, size + size / 2 + r2 * (size + space) + 50, 2 * space + size / 2 + c1 * (size + space) + 40, size + size / 2 + r1 * (size + space) + 50); } else if (gu.yiZhe(r1, c1, r2, c2, array)) { array[r1][c1] = 0; array[r2][c2] = 0; g.setColor(Color.PINK); g.drawLine(2 * space + size / 2 + gu.getPath().get(0).y * (size + space) + 40, size + size / 2 + gu.getPath().get(0).x * (size + space) + 50, 2 * space + size / 2 + c1 * (size + space) + 40, size + size / 2 + r1 * (size + space) + 50); g.drawLine(2 * space + size / 2 + gu.getPath().get(0).y * (size + space) + 40, size + size / 2 + gu.getPath().get(0).x * (size + space) + 50, 2 * space + size / 2 + c2 * (size + space) + 40, size + size / 2 + r2 * (size + space) + 50); } else if (gu.erZhe(r1, c1, r2, c2, array)) { array[r1][c1] = 0; array[r2][c2] = 0; g.setColor(Color.PINK); g.drawLine(2 * space + size / 2 + gu.getPath().get(1).y * (size + space) + 40, size + size / 2 + gu.getPath().get(1).x * (size + space) + 50, 2 * space + size / 2 + c1 * (size + space) + 40, size + size / 2 + r1 * (size + space) + 50); // path的下标为一的位置要减一,因为数组扩大了 g.drawLine(2 * space + size / 2 + (gu.getPath().get(0).y - 1) * (size + space) + 40, size + size / 2 + (gu.getPath().get(0).x - 1) * (size + space) + 50, 2 * space + size / 2 + gu.getPath().get(1).y * (size + space) + 40, size + size / 2 + gu.getPath().get(1).x * (size + space) + 50); g.drawLine(2 * space + size / 2 + (gu.getPath().get(0).y - 1) * (size + space) + 40, size + size / 2 + (gu.getPath().get(0).x - 1) * (size + space) + 50, 2 * space + size / 2 + c2 * (size + space) + 40, size + size / 2 + r2 * (size + space) + 50); } //实现消除控制重绘的刷新时间 Thread t=new Thread(); try { t.sleep(100); frame.repaint(); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } if (isWin(array)) { tt.setFlag(false); frame.removeMouseListener(this); JOptionPane.showMessageDialog(frame, "恭喜你," + "你赢了!!请点击新游戏开始新一局"); int i = JOptionPane.showConfirmDialog(frame, "是否记录将你的信息记入排行榜", "排行榜", JOptionPane.YES_NO_OPTION); if (i == 0) { String str = JOptionPane.showInputDialog(frame, "请输入你的名字", "排行榜", JOptionPane.YES_NO_OPTION); int time=90-tt.getSeconds(); User u = new User(str, time); GameSave gs = new GameSave(); gs.save(u); } } } //未实现消除,重绘去掉线条 if (flag) { Thread t=new Thread(); try { t.sleep(100); frame.repaint(); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } // 按钮动作监听 public void actionPerformed(ActionEvent e) { String str = e.getActionCommand(); if ("新游戏".equals(str)) { for (int r = 0; r < array.length; r++) for (int c = 0; c < array[r].length; c++) if (array[r][c] != 0) { array[r][c] = 0; } if(tt!=null){ if(tt.isFlag()){ frame.removeMouseListener(this); tt.setFlag(false); } } randomData(); frame.repaint(); frame.addMouseListener(this); // 启动线程 tt = new TimeOut(timeJl, frame, this); if(!tt.isFlag()) tt.setFlag(false); tt.start(); } if ("排行榜".equals(str)) { GameSave gs = new GameSave(); List<User> list = gs.opean(); for (int i = 0; i < list.size(); i++) { int flag = i; for (int j = i + 1; j < list.size(); j++) { if (list.get(i).getTime() > list.get(j).getTime()) flag = j; } if (flag != i) { User u1 = list.get(i); User u2 = list.get(flag); list.set(i, u2); list.set(flag, u1); } } JFrame jf = new JFrame(); jf.setTitle("排行榜"); jf.setDefaultCloseOperation(2); jf.setSize(300, 500); FlowLayout fl = new FlowLayout(FlowLayout.LEFT); jf.setLayout(fl); jf.setLocationRelativeTo(null); for (int i = 0; i < list.size(); i++) { JLabel jl = new JLabel(list.get(i).toString()); jl.setFont(new FontUIResource("楷体", Font.BOLD, 20)); jf.add(jl); } jf.setVisible(true); } if("存档".equals(str)){ System.out.println(23333); GameSave2 gs2=new GameSave2(); int time=tt.getSeconds(); CunD c=new CunD(array, time); boolean is=gs2.save(c); if(is) JOptionPane.showMessageDialog(frame, "存档成功!"); else JOptionPane.showMessageDialog(frame, "存档失败!"); } } /** * 生成随机数字 */ public void randomData() { Random random = new Random(); int r1, r2, c1, c2; for (int i = 0; i < array.length * array[0].length / 2; i++) { do { r1 = random.nextInt(array.length); c1 = random.nextInt(array[r1].length); } while (array[r1][c1] != 0); array[r1][c1] = random.nextInt(num) + 1; do { r2 = random.nextInt(array.length); c2 = random.nextInt(array[r2].length); } while (array[r2][c2] != 0); array[r2][c2] = array[r1][c1]; } } //遍历数组,判断输赢 public boolean isWin(int[][] array) { for (int r = 0; r < array.length; r++) for (int c = 0; c < array[r].length; c++) if (array[r][c] != 0) return false; return true; } }
这里的图片我使用的是直接绘制图片,而不是通过按钮,所以坐标的判断有些麻烦。数组的下标取得是通过鼠标坐标整除方格的边长加间隔,然后由数组下标取得屏幕坐标则相反。初始数据是由randomData方法生成,不同的数字对应不同的图片。
连连看的算法:这里我使用的算法是比较容易理解和实现的分类算法,据说还有一种比较厉害的算法叫广度优先搜索,那个我不会,所以就只能用这种了。先说说连连看的规则吧,就是用不超过两次拐弯的直线能够相连就能够消除。这样分类算法就很好理解了,按照消除要拐弯的次数分为无折相连,一折相连和二折相连。首先是无折相连这个很好理解,要么他们左右相邻或者是上下相邻,要么就是同一行,两个方快中间没有阻隔或者是同一列中间没有阻隔。
就像上面的图,左边是不相邻的,右边是相邻的,这两种都属于无折的相连。然后是一折的相连。一折的相连就是拐一个弯,先看看示意图把:
其实无论是哪种情况,能够实现一折相连的方块都是在矩形的两个对顶角,所以只要判断矩形的另外两个对顶角是否能够实现无折相连就行了。最后是二折相连,同样是先看示意图:
二折的情况看似复杂,其实只要在水平方向上和垂直方向上分别进行遍历,如果是空格就判断这个空格是否能够和另一个格子一折相连就行了。其实整个算法有点像是递归,一折调用无折,二折调用一折。算法的思路大概就是这样。然后就上代码吧:
package com.cbs.look; import java.awt.Point; import java.util.ArrayList; import java.util.List; import javax.swing.JFrame; /** * 核心算法 * 判断两个方块是否联通 * @author CBS * */ public class GameUtil implements LookConfig { //path主要是记录下相连的数组的位置,为了方便实现连线的功能 private List<Point> path=new ArrayList<Point>(); public List<Point> getPath() { return path; } public GameUtil(JFrame frame) { } /** * 无折算法,无折的情况,要么同行,判断列是否连通;要么同列判断行是否连通 * * @param r1第一个方块行下标 * @param c1第一个方块列下标 * @param r2第二个方块行下标 * @param c2第二个方块列下标 * @param array用于保存数组的信息 * @return 如果能够连通返回TRUE,or返回FALSE */ public boolean wuZhe(int r1, int c1, int r2, int c2, int[][] array) { if (r1 != r2 && c1 != c2) return false; // 如果两点的x坐标相等,则在水平方向上扫描 if (r1 == r2) { if (c1 == c2 - 1 || c2 == c1 - 1)// 列相邻 return true; for (int i = Math.min(c1, c2) + 1; i < Math.max(c2, c1); i++) if (array[r1][i] != 0) return false; } // 如果两点的y坐标相等,则在竖直方向上扫描 else if (c1 == c2) { if (r1 == r2 - 1 || r2 == r1 - 1)// 行相邻 return true; for (int i = Math.min(r1, r2) + 1; i < Math.max(r2, r1); i++) if (array[i][c1] != 0) return false; } return true; } /** * 一折算法,无论是哪种情况下,都只需要判断对角的r1,c2和r2,c1和两点是否连通 * * @param r1第一个方块行下标 * @param c1第一个方块列下标 * @param r2第二个方块行下标 * @param c2第二个方块列下标 * @param array 用于保存数组的信息 * @return 如果能够连通返回TRUE,or返回FALSE */ public boolean yiZhe(int r1, int c1, int r2, int c2, int[][] array) { // 如果属于0折的情况,直接返回FALSE if (r1 == r2 || c1 == c2) return false; // 测试对角点1 if (array[r1][c2] == 0) { boolean test1 = wuZhe(r1, c1, r1, c2, array); boolean test2 = test1 ? wuZhe(r1, c2, r2, c2, array) : test1; if (test1 && test2){ path.add(new Point(r1,c2)); return true; } } // 测试对角点2 if (array[r2][c1] == 0) { boolean test1 = wuZhe(r1, c1, r2, c1, array); boolean test2 = test1 ? wuZhe(r2, c1, r2, c2, array) : test1; if (test1 && test2){ path.add(new Point(r2,c1)); return true; } } return false; } /** * 二折算法 * * @param r1第一个方块行下标 * @param c1第一个方块列下标 * @param r2第二个方块行下标 * @param c2第二个方块列下标 * @param array用于保存数组的信息 * @return 如果能够连通返回TRUE,or返回FALSE */ public boolean erZhe(int r1, int c1, int r2, int c2, int[][] array) { //在原来数组的基础上扩大一圈,用于判断边界的方格 int[][] newArray = new int[array.length + 2][array[0].length + 2]; for (int r = 0; r < array.length; r++) { for (int c = 0; c < array[r].length; c++) { newArray[r + 1][c + 1] = array[r][c]; } } //判断是否二折连接 // 向下垂直遍历 for (int i = r1 + 2; i < newArray.length; i++) { if (newArray[i][c1+1] == 0 ) { if(yiZhe(r2+1, c2+1, i, c1+1, newArray)){ path.add(new Point(i-1, c1)); return true; } }else break; } // 向上垂直遍历 for (int i = r1 ; i > -1; i--) { if (newArray[i][c1+1] == 0 ){ if(yiZhe(i, c1+1, r2+1, c2+1, newArray)){ path.add(new Point(i-1, c1)); return true; } }else break; } // 向右水平遍历 for (int i = c1 + 2; i < newArray[0].length; i++) { if (newArray[r1+1][i] == 0 ){ if( yiZhe(r2+1, c2+1, r1+1, i, newArray)){ path.add(new Point(r1,i-1)); return true; } }else break; } // 向左水平遍历 for (int i = c1 ; i > -1; i--) { if (newArray[r1+1][i] == 0 ) { if(yiZhe(r1+1, i, r2+1, c2+1, newArray)){ path.add(new Point(r1,i-1)); return true; } }else break; } return false; } }
还有一个要说一下就是上面第一张图的那种二折情况,如果这种二折情况处于数组的边界的话,在使用上面的二折算法判断就会因为数组的问题,永远不可能实现相连。解决的方法就是:把数组扩大,在整个的数组外边加上一层,也就是说原来4*4变成6*6。把所有除原来4*4之外的位置的值都设为0,这样就能够遍历到了。不过在判断的时候要注意使用的是新数组的坐标还是旧数组的坐标(把新数组的坐标行下标和列下标都减一就是旧数组的行下标和列下标)。能够消除就意味着你的连连看游戏已经可以玩了,剩下的就是一些优化的问题了。
排行榜和存档功能的实现:这两个功能放一起讲,因为都用到了Java的IO的一些知识。这里主要用到的是Java中的FileOutputStream和FileInputStream这两个类。FileOutputStream这个类是把内存中的数据输出到外部的存储设备,主要的方法就是write(向目标文件一次写入一个字节)和close(关闭输出流);FileInputStream则是把外部的数据输入到内存中供程序使用,主要的方法就是read(从目标文件一次读取一个字节)和close(关闭输入流)。存档的思路就是:把当前的数组情况和时间记录下来(使用一个类来保存这两个信息),保存到一个文件中,下次打开游戏的时候询问用户是否载入进度。排行榜的话是如果玩家获胜,就记录下玩家的姓名和通关用时(同样使用一个类),保存到文件中。下面是代码,把存档和排行榜的相关类都放进去了:
1 package com.cbs.look; 2 3 /** 4 * 存档 5 * 6 * @author CBS 7 */ 8 9 public class CunD { 10 private int array[][]; 11 private int time; 12 13 public CunD() { 14 } 15 16 public CunD(int[][] array, int time) { 17 this.array = array; 18 this.time = time; 19 } 20 21 public int[][] getArray() { 22 return array; 23 } 24 25 public void setArray(int[][] array) { 26 this.array = array; 27 } 28 29 public int getTime() { 30 return time; 31 } 32 33 public void setTime(int time) { 34 this.time = time; 35 } 36 37 } 38 package com.cbs.look; 39 40 import java.io.FileInputStream; 41 import java.io.FileNotFoundException; 42 import java.io.FileOutputStream; 43 import java.io.IOException; 44 import java.io.InputStream; 45 import java.io.OutputStream; 46 47 /** 48 * 用于保存游戏存档的信息 49 * 50 * @author CBS 51 */ 52 public class GameSave2 { 53 54 public boolean save(CunD d) { 55 56 String path = "src/com/cbs/look/info.txt"; 57 58 try { 59 60 OutputStream os = new FileOutputStream(path); 61 os.write(d.getTime()); 62 for (int r = 0; r < d.getArray().length; r++) { 63 for (int c = 0; c < d.getArray()[0].length; c++) { 64 os.write(d.getArray()[r][c]); 65 } 66 } 67 os.close(); 68 return true; 69 } catch (FileNotFoundException e) { 70 e.printStackTrace(); 71 } catch (IOException e) { 72 e.printStackTrace(); 73 } 74 75 return false; 76 } 77 78 public CunD opean() { 79 String path = "src/com/cbs/look/info.txt"; 80 try { 81 InputStream is = new FileInputStream(path); 82 int time = is.read(); 83 int array[][]=new int[8][8]; 84 85 for (int i = 0; i < array.length; i++) 86 for (int j = 0; j < array[0].length; j++) 87 array[i][j] = is.read(); 88 CunD c=new CunD(array,time); 89 is.close(); 90 return c; 91 92 } catch (FileNotFoundException e) { 93 e.printStackTrace(); 94 } catch (IOException e) { 95 e.printStackTrace(); 96 } 97 return null; 98 } 99 } 100 package com.cbs.look; 101 102 /** 103 * 记录排行榜的用户信息的类 104 * 105 * @author CBS 106 * 107 */ 108 public class User { 109 110 private String name;//用户名 111 112 private int time;//记录用户通过所用时间 113 114 public User() { 115 } 116 117 public User(String name, int time) { 118 this.name = name; 119 this.time = time; 120 } 121 122 public String getName() { 123 return name; 124 } 125 126 public void setName(String name) { 127 this.name = name; 128 } 129 130 public int getTime() { 131 return time; 132 } 133 134 public void setTime(int time) { 135 this.time = time; 136 } 137 138 @Override 139 public String toString() { 140 String str=name+"用时为:"+time; 141 return str; 142 } 143 144 145 } 146 package com.cbs.look; 147 148 import java.io.DataInputStream; 149 import java.io.DataOutputStream; 150 import java.io.FileInputStream; 151 import java.io.FileNotFoundException; 152 import java.io.FileOutputStream; 153 import java.io.IOException; 154 import java.io.InputStream; 155 import java.io.OutputStream; 156 import java.util.ArrayList; 157 import java.util.List; 158 159 /** 160 * 用于保存游戏排行榜信息 161 * 162 * @author CBS 163 */ 164 public class GameSave { 165 166 public boolean save(User user) { 167 168 String path = "src/com/cbs/look/save3.txt"; 169 170 try { 171 List<User> array = opean(); 172 if(array!=null){ 173 array.add(user); 174 for (int i=0;i<array.size()-1;i++) { 175 int flag=i; 176 for(int j=i+1;j<array.size();j++){ 177 if(array.get(i).getTime()>array.get(j).getTime()) 178 flag=j; 179 } 180 if(flag!=i){ 181 User u1=array.get(i); 182 User u2=array.get(flag); 183 array.set(i, u2); 184 array.set(flag, u1); 185 } 186 } 187 }else{ 188 array=new ArrayList<User>(); 189 array.add(user); 190 } 191 192 OutputStream os = new FileOutputStream(path); 193 DataOutputStream dos=new DataOutputStream(os); 194 //先写入有几条信息 195 os.write(array.size()); 196 for (int i = 0; i < array.size(); i++) { 197 User u = array.get(i); 198 dos.writeByte(u.getName().getBytes().length); 199 dos.write(u.getName().getBytes()); 200 dos.writeInt(u.getTime()); 201 } 202 os.close(); 203 dos.close(); 204 return true; 205 } catch (FileNotFoundException e) { 206 e.printStackTrace(); 207 } catch (IOException e) { 208 e.printStackTrace(); 209 } 210 211 return false; 212 } 213 214 public List<User> opean() { 215 String path = "src/com/cbs/look/save3.txt"; 216 try { 217 InputStream is = new FileInputStream(path); 218 DataInputStream dis = new DataInputStream(is); 219 220 //读取有几条信息 221 int size=is.read(); 222 List<User> list=new ArrayList<User>(); 223 224 if(size!=-1){ 225 for(int i=0;i<size;i++){ 226 byte bsize=dis.readByte(); 227 byte[] b=new byte[bsize]; 228 is.read(b); 229 int time=dis.readInt(); 230 User u=new User(new String(b),time); 231 list.add(u); 232 } 233 } 234 dis.close(); 235 is.close(); 236 return list; 237 238 } catch (FileNotFoundException e) { 239 e.printStackTrace(); 240 } catch (IOException e) { 241 e.printStackTrace(); 242 } 243 return null; 244 } 245 }
倒计时的实现:倒计时其实很简单使用一个线程就好(如果线程不了解请自行百度或者学习,不用很了解,知道就行,只是为了做个倒计时罢了),每次线程休眠一秒就让时间减一,然后把时间在窗体中输出就行了。也没什么可说的,上代码咯:
package com.cbs.look; /** * 线程类,控制时间 */ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; public class TimeOut extends Thread { private int seconds=90;//游戏时间 private JFrame frame;//主窗体对象 private JLabel jl;//倒计时标签 private boolean flag = true;// 控制线程结束 private GameListener gl;//事件处理类 // 在别的类中控制线程的关闭 public void setFlag(boolean flag) { this.flag = flag; } public boolean isFlag() { return flag; } public int getSeconds() { return seconds; } public void setSeconds(int seconds) { this.seconds = seconds; } public TimeOut(JLabel jl, JFrame frame, GameListener gl) { this.jl = jl; this.frame = frame; this.gl = gl; } @Override public void run() { // seconds = 90; jl.setText(seconds + ""); while (seconds-- > 0 && flag) { jl.setText(seconds + ""); if (seconds == 0) { JOptionPane.showMessageDialog(frame, "不好意思,时间用光了,请开始新游戏"); frame.removeMouseListener(gl); } try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
这样所有的东西也就都讲完了,剩下的就是如何把它们组织在一起形成你的东西了。