简介

羊了个羊游戏爆火,就是太难玩了,我玩了几十次,玩不过去,很纠结,作为技术人员的我,忍不了,就抽了5个小时用Java实现了一个桌面版本,效果如下:

测试现场

羊了个羊开发现场

实现思路+代码实现

实现步骤:先画界面,给界面添加上逻辑。

第一步:画界面——界面分区

把界面分成叠卡区、翻牌区、验卡区三个部分,然后一个区域的话。

第一步:画叠卡区——实现思路

叠卡区又分成三步来实现:

  • 生成卡片:生成所需要卡片,不放到一个卡片集合中,注意顺序要打乱

  • 摆放卡片:把生成的卡片摆放对应区域、对应层次

  • 错落有致:让上下层的卡片有错落感

第一步:画叠卡区——生成卡片思路

  • 取一张图片按照下面3部生成

  • 取第二张图片重复上面过程

  • 最后把所有图案都按照上述过程实现一遍,即可得到一个随机乱序的卡片集合

以上思路实现的参考代码如下: 

 int maxLevel = 10;//多少层
        int maxWidth = 6;// 跨度个数
        int maxHeight = 5;// 最大宽度
        int maxFlop = 60;//翻牌数量;
        Random random = new Random();
        // 如果需要随机皮肤,修改为true即可
        List<String> list = ReadResourceUtil.readSkin(true);
        int typeSize = list.size();// 多少种类
        System.out.println("种类:"+typeSize);
        int groupNumber = (int) Math.ceil((maxLevel*maxWidth*maxHeight+maxFlop)/(3f*typeSize));// 求得每种种类的个数
        System.out.println("每种组数:"+groupNumber);
        int groupCount = groupNumber*3;
        System.out.println("每种总数:"+groupCount);
        System.out.println("共计数量:"+(typeSize*groupCount+maxFlop));
        // 绘制卡槽
        int initX = 100;
        int initY = 50;
        CardSlotCantainer cardSlotCantainer = new CardSlotCantainer(imageCantainer,initX+((maxWidth-7)*FruitObject.defaultWidht)/2,+initY+FruitObject.defaultHeight*(maxHeight+2));
        // 随机生成卡片集合:注意打乱顺序
        List<FruitObject> objects = new ArrayList<>();
        for (String temp : list) {
            try {
                BufferedImage bufferedImage = ImageIO.read(ReadResourceUtil.getUri("/"+temp));
                int count = groupCount+(maxFlop>0?random.nextInt(maxFlop):0);
                for (int i = 0; i < count; i++) {
                    int size = objects.size()-1;
                    Fruits fruits = new Fruits(imageCantainer,bufferedImage,temp);
                    fruits.setPreferredSize(new Dimension(100, 100));
                    int index = 0;
                    if(size>10) {
                        index = random.nextInt(size);
                    }
                    objects.add(index, new FruitObject(cardSlotCantainer,fruits, 0, 0, 0));
                    maxFlop--;
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("实际数量:"+objects.size());

第一步:画叠卡区——摆放卡片思路

  • 直接从上一步集合中取出卡片依此摆放到对应层的卡片位置

  • 当一层摆放完成后,循环摆放下一层,以此类推

以上思路实现的参考代码如下:  

// 给每个对象设置坐标
        int index = 0;
        for (int i = 0; i < maxLevel; i++) {
            for (int x = 0; x < maxWidth; x++) {
                for (int y = 0; y < maxHeight; y++) {
                    FruitObject fruitObject = objects.get(index++);
                    fruitObject.setX(x);
                    fruitObject.setY(y);
                    fruitObject.setLevel(i);
                    fruitObject.show(imageCantainer,initX-FruitObject.defaultWidht/4,initY-FruitObject.defaultHeight/4);
                }
            }
        }
        System.out.println("重叠数量:"+index);

第一步:画叠卡区——卡片错落感实现思路

  • 给上层卡片的地点x、y值增加随机值,即可实现层与层之间的卡片错落感

以上思路实现的参考代码如下:  

    /**
     * 添加到叠卡区
     * @param imageCantainer
     * @param initX
     * @param initY
     */
    public void show(ImageCantainer imageCantainer, int initX, int initY) {
        this.imageCantainer = imageCantainer;
        // 随机生成开始坐标偏移量,实现上下层错落有致的视觉感
        boolean ranDomWidth = RANDOM.nextInt(10)%2==0;
        boolean ranDomHeight = RANDOM.nextInt(10)%2==0;
        int pointX = initX + x*defaultWidht+(ranDomWidth?defaultWidht/2:0);
        int pointY = initY + y*defaultHeight+(ranDomHeight?defaultHeight/2:0);
        // 设置卡片显示在背景面板中位置
        fruits.setBounds(pointX,pointY,defaultWidht,defaultHeight);
        // 记录卡片的空间信息
        SpaceManager.rectangle(this);
        imageCantainer.add(fruits,0);
        addClick();
    }

第一步:画翻牌区——实现思路

翻牌区实现的思路和叠卡区类似,少一步错落有致的步骤。因此大家参考上述的思路理解即可。

以上思路实现的参考代码如下:  

/**
     * 添加到翻牌区
     * @param imageCantainer
     * @param initX
     * @param initY
     * @param offset
     * @param isLeft
     */
    public void showFold(ImageCantainer imageCantainer, int initX, int initY, int offset,boolean isLeft) {
        this.imageCantainer = imageCantainer;
        // 随机生成开始坐标偏移量,实现上下层错落有致的视觉感
        int pointX = initX + x*defaultWidht+offset;
        int pointY = initY + y*defaultHeight-defaultHeight/4;
        if(isLeft){
            this.leftFold = true;
        }else{
            this.rightFold= true;
        }
        // 设置卡片显示在背景面板中位置
        fruits.setBounds(pointX,pointY,defaultWidht,defaultHeight);
        // 记录卡片的空间信息
        SpaceManager.rectangle(this);
        imageCantainer.add(fruits,0);
        addClick();
    }

第一步:画验卡区——实现思路

验卡区可以用两个圆角长方形直接重叠实现即可。

以上思路实现的参考代码如下:  

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        BasicStroke basicStroke = new BasicStroke(borderSize);
        g2d.setStroke(basicStroke);
        // 绘制第1层底色
        g2d.setColor(bgColor);
        g2d.fillRoundRect(0, 0, (int) (getSize().width - borderSize), (int) (getSize().height - borderSize),arc,arc);
        // 绘制第2层底色
        g2d.setColor(borderColor);
        g2d.fillRoundRect(borderSize, borderSize, (int) (getSize().width - 1-borderSize*3), (int) (getSize().height - 1-borderSize*3),arc,arc);
        super.paintComponent(g);
    }

第二步:实现界面逻辑控制——实现思路

每个去都有自己的界面控制逻辑,如下图,具体内容就可以参考代码了

以上思路实现的参考代码如下:  

public void addSlot(FruitObject object){
        if(isOver){
            return;
        }
        slot.add(object);
        // 验卡区的卡片删除点击事件
        object.removeImageCantainer();
        MouseListener[] mouseListeners = object.getFruits().getMouseListeners();
        if(mouseListeners!=null){
            for (MouseListener mouseListener : mouseListeners) {
                object.getFruits().removeMouseListener(mouseListener);
            }
        }
        // 排序验卡区中的图片
        slot.sort(Comparator.comparing(FruitObject::getImageName));
        // 3张图片的判断,如果有直接消除,思路是:分组后看每组数量是否超过3张如果超过则消除
        Map<String, List<FruitObject>> map = slot.stream().collect(Collectors.groupingBy(FruitObject::getImageName));
        Set<String> keys = map.keySet();
        for (String key : keys) {
            List<FruitObject> objects = map.get(key);
            if(objects.size()==3){
                if(audioClip!=null){
                    audioClip.play();
                }
                // 消除的元素直接从集合中删除
                for (FruitObject fruitObject : objects) {
                    fruitObject.removeCardSlotCantainer();
                }
                slot.removeAll(objects);
            }
        }
        // 新添加的卡片,显示到验卡区
        redraw();
        // 判断游戏是否结束
        if(slot.size()==solt){
            isOver = true;
            failClip.play();
            JOptionPane.showMessageDialog(this.getParent(), "Game Over:槽满了","Tip",JOptionPane.ERROR_MESSAGE);
        }
    }

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐