基于Java的练练看小游戏(大作业)(附源码以及图片资源)

本文详细介绍了作者使用JavaSwing开发的一个基于连连看的游戏,涉及效果展示、不同类型的连接算法(无拐点、单拐点和双拐点)、图片读取、鼠标事件处理、地图和棋盘绘制,以及图片消失的实现。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

  这是我面向对象程序设计课程的大作业,希望能够帮到大家。因为这个项目中一些设计需要用到矢量图才能完成,我找了许久发现我的世界的素材比较好用,于是就整体走的是我的世界风。


一、效果展示

话不多说,直接上图。用swing做的。

1.游戏主界面

2.游戏内界面 



3.排行榜 

 

二、连连看算法

连连看最重要的算法应该是连接的算法了,连连看里有三种连接方式:无拐点连接,单拐点连接,双拐点连接。 

1.无拐点连接

 

最简单的一种吧,不做过多解释。 

代码如下(示例):

 //在以下连接判断中,是在map这个二维数组中判断的,二维数组中的坐标是[y][x]
    //写法是[clickX][clickY]。把ClickX当作y,把ClickY当作x
    private boolean zeroCornerLink(int clickX1, int clickY1, int clickX2, int clickY2) {

        if (clickX1 == clickX2) {//如果两个选中图片的所在行数相同,说明可能可以水平相联

            if (clickY1 > clickY2) {//保证y1<y2
                int temp = clickY1;
                clickY1 = clickY2;
                clickY2 = temp;
            }

            for (int i = clickY1 + 1; i < clickY2; i++) {
                if (map[clickX1][i] != BLANK_STATE) {//如果两图片中间不空则无法相连
                    return false;
                }
            }
            linkMethod = ZEROCORNER;
            return true;
        }
        if (clickY1 == clickY2) {//两个图片处于同一列

            if (clickX1 > clickX2) {//让第一个图片在上方
                int temp = clickX1;
                clickX1 = clickX2;
                clickX2 = temp;
            }
            for (int i = clickX1 + 1; i < clickX2; i++) {
                if (map[i][clickY1] != BLANK_STATE) {//如果两图片中间还有其他图片,说明不能直接垂直相连
                    return false;
                }
            }
            linkMethod = ZEROCORNER;
            return true;
        }
        return false;
    }

2.单拐点连接

对于单拐点连接来说,拐点的x与一个相同,y与另一个相同。对于单拐点连接问题可以转换成两个拐点能否与两个点进行无拐点连接。
比如图中的(x1,y1)与(x2,y2)能单拐点连接则只需满足以下两种情况的任意一种:

1.(x1,y1)与(x1,y2)无拐点连接,(x2,y2)与(x1,y2)无拐带点连接
2. (x1,y1)与(x2,y1)无拐点连接,(x2,y2)与(x2,y1)无拐带点连接
 

//一个拐点的情况,拐点横坐标与一个相同,纵坐标与另一个相同
    private boolean oneCornerLink(int clickX1, int clickY1, int clickX2, int clickY2) {
        if (map[clickX1][clickY2] == BLANK_STATE && zeroCornerLink(clickX1, clickY2, clickX1, clickY1) && zeroCornerLink(clickX1, clickY2, clickX2, clickY2)) {
            linkMethod = ONECORNER;
            z1 = new Node(clickX1, clickY2);
            return true;
        }
        if (map[clickX2][clickY1] == BLANK_STATE && zeroCornerLink(clickX2, clickY1, clickX1, clickY1) && zeroCornerLink(clickX2, clickY1, clickX2, clickY2)) {
            linkMethod = ONECORNER;
            z1 = new Node(clickX2, clickY1);
            return true;
        }
        return false;
    }

 3.双拐点连接

 图画的可能有点抽象,我的算法也可能不是最优的,但我的方法是这样的:点1(x1,y1)与点2(x2,y2)想要连接,那么假设从点1开始出发,先往上走一个格子,然后用新位置与点2判断是否可以进行单拐点连接,如果可以那么新位置算一个拐点,新位置和点2单拐点连接又有一个拐点,加起来就算双拐点了。如果不能进行单拐点连接,那么就继续往上走,如果走到顶了,还不行?那么回到点1位置往左往右往下一直走。

//判断是否可以通过两个拐点相连,思路是通过上下左右走直线,每走一步便用这一步的地点和目标地点用单拐点连接
    private boolean twoCornerLink(int clickX1, int clickY1, int clickX2, int clickY2) {

        //向上查找
        for (int i = clickX1 - 1; i >= -1; i--) {

            if (i > -1) {
                if (map[i][clickY1] == BLANK_STATE) {
                    if (oneCornerLink(i, clickY1, clickX2, clickY2)) {
                        linkMethod = TWOCORNER;
                        z1 = new Node(i, clickY1);
                        z2 = new Node(i, clickY2);
                        return true;
                    }
                } else
                    break;

            } else if (i == -1 && zeroCornerLink(i, clickY2, clickX2, clickY2)) {
                linkMethod = TWOCORNER;
                z1 = new Node(i, clickY1);
                z2 = new Node(i, clickY2);
                return true;
            }
        }

        //向下查找
        for (int i = clickX1 + 1; i <= n; i++) {
            if (i < n) {
                if (map[i][clickY1] == BLANK_STATE) {
                    if (oneCornerLink(i, clickY1, clickX2, clickY2)) {
                        linkMethod = TWOCORNER;
                        z1 = new Node(i, clickY1);
                        z2 = new Node(i, clickY2);
                        return true;
                    }
                } else
                    break;
            } else if (i == n && zeroCornerLink(i, clickY2, clickX2, clickY2)) {
                linkMethod = TWOCORNER;
                z1 = new Node(i, clickY1);
                z2 = new Node(i, clickY2);
                return true;
            }
        }

        //向左查找
        for (int i = clickY1 - 1; i >= -1; i--) {

            if (i > -1) {
                if (map[clickX1][i] == BLANK_STATE) {
                    if (oneCornerLink(clickX1, i, clickX2, clickY2)) {
                        linkMethod = TWOCORNER;
                        z1 = new Node(clickX1, i);
                        z2 = new Node(clickX2, i);
                        return true;
                    }
                } else
                    break;
            } else if (i == -1 && zeroCornerLink(clickX2, i, clickX2, clickY2)) {
                linkMethod = TWOCORNER;
                z1 = new Node(clickX1, i);
                z2 = new Node(clickX2, i);
                return true;
            }
        }

        //向右查找
        for (int i = clickY1 + 1; i <= n; i++) {

            if (i < n) {
                if (map[clickX1][i] == BLANK_STATE) {
                    if (oneCornerLink(clickX1, i, clickX2, clickY2)) {
                        linkMethod = TWOCORNER;
                        z1 = new Node(clickX1, i);
                        z2 = new Node(clickX2, i);
                        return true;
                    }
                } else
                    break;
            } else if (i == n && zeroCornerLink(clickX2, i, clickX2, clickY2)) {
                linkMethod = TWOCORNER;
                z1 = new Node(clickX1, i);
                z2 = new Node(clickX2, i);
                return true;
            }
        }
        return false;
    }

三、 杂项

     连连看的算法虽然很简单,但大家可能会卡在一些根连连看算法无关的地方,回顾我写这个大作业的历程,我觉得有必要把一些觉得比较重要的东西告诉大家。

1.怎么把图片读进去?

//从Picture里面读取图片

    private void getPics() {
        pics = new Image[n];
        for (int i = 0; i < n; i++) {
            pics[i] = Toolkit.getDefaultToolkit().getImage("Picture/" + (i + 1) + ".png");
        }
    }


  

 2.你是怎么做到点击连连看里的图标的?

       我的这个项目是点击不了图片的,我一开始想做图片按钮,就是把按钮里填充图片,然后用按钮实现,但是黄了,不会弄。后面参考别人的资料后我就学着改为获取鼠标点击界面的xy坐标,然后进行后续的操作。 

        先看代码:

 @Override
    public void mousePressed(MouseEvent e) {
        // TODO Auto-generated method stub

        Graphics g = this.getGraphics();
        int x = e.getX() - leftX;//点击位置x-偏移量x
        int y = e.getY() - leftY;//点击位置y-偏移量y
        int i = y / imageHeight;//对应数组行数,根据像素坐标转换成数组下标坐标
        int j = x / imageWidth;//对应数组列数
//        if(i<0||j<0||i>n||j>n)//超出地图范围
//            return;
        if (Enabled) {
            if (!isClick) {//如果是第一次点击

                if (map[i][j] != BLANK_STATE) {
                    //选中图片并画框
                    clickId = map[i][j];
                    isClick = true;
                    clickX = i;
                    clickY = j;
                    drawSelectedBlock(j * imageWidth + leftX, i * imageHeight + leftY, g);
                }

            } else {//第二次点击了
                if (map[i][j] != BLANK_STATE) {
                    if (map[i][j] == clickId) {//点击的是同一种图片
                        //两次选同一位置的图片解除选中状态
                        if (i == clickX && j == clickY) {
                            clearSelectBlock(clickX, clickY, g);
                            isClick = false;
                        }
                        //如果可以连通,画线连接,然后消去选中图片并重置第一次选中标识
                        else if (zeroCornerLink(clickX, clickY, i, j) || oneCornerLink(clickX, clickY, i, j) || twoCornerLink(clickX, clickY, i, j)) {
                            drawSelectedBlock(j * imageWidth + leftX, i * imageHeight + leftY, g);
                            drawLink(clickX, clickY, i, j);//画线连接
                            isClick = false;

                        } else {//相同图片但连接失败,把选定框给新选的
                            clearSelectBlock(clickX, clickY, g);
                            clickId = map[i][j];
                            clickX = i;
                            clickY = j;
                            drawSelectedBlock(j * imageWidth + leftX, i * imageHeight + leftY, g);
                        }

                    } else {//选的图片都不是同一种,把选定框给新的
                        clearSelectBlock(clickX, clickY, g);
                        clickId = map[i][j];//重新选中图片并画框
                        clickX = i;
                        clickY = j;
                        drawSelectedBlock(j * imageWidth + leftX, i * imageHeight + leftY, g);
                    }

                }

            }
        }
    }

如果你认真看完了代码,我相信你应该知道了,我定了一个地图数组,当我“点击”图片时,点击的xy值会经过一些列计算映射为一个map[i][j],然后后续的判断是否能连接,或者是画线消去之类的操作都只跟映射的map[i][j]有关。

 3.你是如何实现map的? 

 我嘴笨,代码注释的够多了,请自己先看代码:

import java.util.ArrayList;

public class Map {

    private final int[][] map;
    private final int count;
    private int n;


    public Map(int count, int n) {//一共有count种不同的图案,n行n列
        map = MapFactory.getMap(n);//获取n行n列的数组
        this.count = count;
        this.n = n;
    }


    public int[][] getMap(int m) {
        n = m;
        ArrayList<Integer> list = new ArrayList<Integer>();//先将等量图片ID添加到list中


        //
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                list.add(j);
            }
        }

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int index = (int) (Math.random() * list.size());//从list中随机取一个图片ID,并将其添加到map数组中,再从list中删除掉它
                map[i][j] = list.get(index);
                list.remove(index);
            }
        }

        return map;//返回一个图片随机生成的地图数组

    }


    public int[][] getResetMap() {//获取再次打乱后的地图信息

        ArrayList<Integer> list = new ArrayList<Integer>();//list用来存储原先的地图信息

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (map[i][j] != -1)//如果(x,y)有图片,那么将该图片添加到list
                    list.add(map[i][j]);
                map[i][j] = -1;//即使原来ID不为-1也要标空,不然后续重新绘图的时候图片会变多
            }
        }

        //将原先地图上剩余的未消去的图片打乱
        while (!list.isEmpty()) {

            int index = (int) (Math.random() * list.size());//从list中随机取一个图片ID,并将其添加到数组中,再从list中删除掉它
            boolean flag = false;

            while (!flag) {
                int i = (int) (Math.random() * n);//获取随机的地图行列
                int j = (int) (Math.random() * n);
                if (map[i][j] == -1) {//如果该位置无图片
                    map[i][j] = list.get(index);
                    list.remove(index);
                    flag = true;
                }
            }

        }

        return map;

    }

    public class MapFactory {

        static int[][] map;

        public static int[][] getMap(int n) {
            map = new int[n][n];//生成n*n地图

            //初始化地图信息为空
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    map[i][j] = -1;
                }
            }

            return map;
        }

    }

}

首先声明,这段代码有借鉴的成分在里面,不是我原创的。

简单来讲就是在游戏开始时,给每个图片编个号,确定每个图片的位置,然后再在界面中画出来。

4.怎么把棋盘/地图画出来?

 //画出棋盘和棋盘上的图片
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        //图片长宽
        imageWidth = getWidth() / (4 + n);
        imageHeight = getHeight() / (2 + n);
        //偏移量
        leftX = getWidth() / (n - 2);
        leftY = getHeight() / (n + 2);
        //画背景图片
        g.drawImage(backgroundImage.getImage(), 0, 0, getWidth(), getHeight(), this);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                g.drawImage(blank.getImage(), leftX + j * imageWidth, leftY + i * imageHeight, imageWidth, imageHeight, this);
                if (map[i][j] != BLANK_STATE) {
                    g.drawImage(pics[map[i][j]], leftX + j * imageWidth, leftY + i * imageHeight, imageWidth, imageHeight, this);
                }
            }
        }
    }

5.如何实现连接图片后图片消失? 
 

连接图片后,图片在map数组中的值会被标为空格子,从数据层面上实现消失。

接着是以下代码,在原来的图片的位置再重新画个空格子就实现了图片的消失。

//清除选中框,用棋盘的格子把原来的地方盖住,然后再在上面画图片
    public void clearSelectBlock(int i, int j, Graphics g) {
        g.drawImage(blank.getImage(), leftX + j * imageWidth, leftY + i * imageHeight, imageWidth, imageHeight, this);
        g.drawImage(pics[map[i][j]], leftX + j * imageWidth, leftY + i * imageHeight, imageWidth, imageHeight, this);
    }


总结

觉得有用的话就点个赞吧。有什么不懂的地方欢迎提问。
链接:https://pan.baidu.com/s/1Ct8ZnhRg9GzBhTEquohdTg?pwd=j9yn 
提取码:j9yn

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
用来完成大作业的。文档内容: 1 Java技术体系 1.1 Java语言 1.2 Java平台 1.3 Java应用领域 2 Java语言的技术特点 2.1 1 2.2 2 2.3 3 3 Java语言与C++的异同分析总结。 4 选用C和java语言时编程算法程序有什么不同,有什么优势和劣势。 5 自己编程学习的级别和状态。以及自己以后的编程学习的计划和想法。 6 下面3道题目中选一道,给出算法分析和程序。 1)“黄金分割数”在我们的生活中很常见,但是在不同的应用领域,要求的精度也不一样。 例如:三位小数是0.618 现在我们需要你能求出保留100位小数的黄金分割数,采用的算法为“分层计算法”: 黄金数= 1 --------------- 1+ 1 ------------- 1+ 1 ----------- 1+ 1 --------- ..... 注意,计算出的结果,如果第100位为0也需要保留。 2)已知一个数列: 5,2,4,3,7,6 那么,在这个数列中存在这样一些“连续数”,例如:5,2,4,3这个子数列排序后是连续的。同样2,4,3也是连续的,为了方便表示 我们使用下标来标识,这样,这个数列中存在以下“连续数”: [1,1] [1,4] [1,6] [2,2] [2,4] [3,3] [3,4] [4,4] [5,5] [5,6] [6,6] 这样,他就存在11个“连续数”。现在需要你在用户找出一个数组中所有的“连续数”。 要求: 1、用户输入一个整数N,表示下面数组的个数 2、用户每输入一行作为一个数组 如: 用户输入: 1 5,2,4,3,7,6 程序输出: 11 3)有一种数我们称之为幸运数,它的特点是这样的,首先,由自然数按顺序排列: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 … 这样,1比较特殊, 1为第一个幸运数,那么,我们移除掉序号能被2整除的数(注意:是序号,而不是数本身,每次移除后都重新排序)就剩下: 1 3 5 7 9 11 13 15 17 19… 3为第二个幸运数,那么我们需要去掉序号能被3(下一次是除4,然后是5,每次加1)整除的数,5 11 17...剩下: 1 3 7 9 13 15 19… 那么7为第三个幸运数,后面的幸运数,依此类推,移除之后剩下的数字都是幸运数。 现在我们需要你求出给定的m和n之间的幸运数的个数: 例如:给定1 20,那么个数为:5(5个幸运数分别是1,3,7,13,19) 现在要求用户输入两个数m和n(m<n<=1000*1000),输出幸运数的个数。 例如: 用户输入: 1 20 程序输出: 5 格式:小四,1.5倍行距
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值