Java版连连看

Posted 迷失的小菜包

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java版连连看相关的知识,希望对你有一定的参考价值。

连连看大家应该都玩过,不多说直接上一个做好的界面截图吧,所有的功能都在上面的,要做的就只是如何去实现它们了。

差不多就是这个样子。先说一下大致的思路吧。首先编写基本的界面:把什么按钮啊,表格啊什么的都画上去。然后就是编写事件处理类,因为操作使用鼠标,所以加上鼠标监听。然后获取点击的坐标,根据坐标得出图片在数组中的位置。接着创建一个类,实现连连看消除的算法。这样就基本上可以开始游戏了。然后实现排行榜按钮和存档按钮的基本功能。最后加上一个线程类,用于处理倒计时。下面的介绍也基于这个顺序。

界面实现:这个其实没什么好说的,把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;//图片类型
}
View Code
  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 }
View Code

事件处理类:鼠标的事件处理主要负责的是记录两次点击的坐标,然后判断是否能够把两个图片消除,如果可以消除图片把对应的数组位置的数置为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;
    }
}
View Code

这里的图片我使用的是直接绘制图片,而不是通过按钮,所以坐标的判断有些麻烦。数组的下标取得是通过鼠标坐标整除方格的边长加间隔,然后由数组下标取得屏幕坐标则相反。初始数据是由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 [vscode]--HTML代码片段(基础版,reactvuejquery)

如何使用eclipse导入文件

关于《连连看》的算法研究和演示Demo

基于java的连连看游戏设计

beta版验收互评

站立会议-----课堂演示版