前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >5小时复刻《羊了个羊》,Java代码已开源,还有108套皮肤

5小时复刻《羊了个羊》,Java代码已开源,还有108套皮肤

作者头像
非喵鱼
发布2022-12-14 09:05:10
5980
发布2022-12-14 09:05:10
举报
文章被收录于专栏:Java技术记号Java技术记号

简介

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

测试现场

羊了个羊开发现场

实现思路+代码实现

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

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

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

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

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

  • 生成卡片:生成所需要卡片,不放到一个卡片集合中,注意顺序要打乱
  • 摆放卡片:把生成的卡片摆放对应区域、对应层次
  • 错落有致:让上下层的卡片有错落感

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

  • 取一张图片按照下面3部生成
  • 取第二张图片重复上面过程
  • 最后把所有图案都按照上述过程实现一遍,即可得到一个随机乱序的卡片集合

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

代码语言:javascript
复制
 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());

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

  • 直接从上一步集合中取出卡片依此摆放到对应层的卡片位置
  • 当一层摆放完成后,循环摆放下一层,以此类推

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

代码语言:javascript
复制
// 给每个对象设置坐标
        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值增加随机值,即可实现层与层之间的卡片错落感

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

代码语言:javascript
复制
    /**
     * 添加到叠卡区
     * @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();
    }

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

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

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

代码语言:javascript
复制
/**
     * 添加到翻牌区
     * @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();
    }

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

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

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

代码语言:javascript
复制
    @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);
    }

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

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

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

代码语言:javascript
复制
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);
        }
    }
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-09-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 测试现场
  • 实现思路+代码实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com