我,7天开发一个《爱上叠叠叠》类小游戏的思考总结

这个游戏在微信小游戏畅玩榜已经有一阵子了,在上榜单的时候也观察了这款游戏,这个游戏到底是怎么实现呢?玩法有点类似《羊了个羊》,但又有点差异。相同的地方是三消游戏,找到相同的三个元素就可以完成消除。差异点是这个游戏的堆叠逻辑不一样,因为每一关都是不同的地图。堆叠地图明显是经过人为设计过,不像《羊了个羊》那么随机,虽然羊也被设计,但显得更加无规律。

那怎么可以简化地图生成的复杂度呢?可以摆放出任意的造型,而又保证游戏的堆叠逻辑。琢磨了很久,发现其实很简单。判断是否发生堆叠的逻辑就是是否发生碰撞。

花朵堆叠

屏幕截图 2024-04-08 204105

刚好在文档编辑器中可以画图比较好示意,如上图,创建多个圆形,本身就是有层级关系,那么最终的效果就有堆叠的效果。只要将压在下面的圆换个深度颜色就可以,在cocos中给Node节点设置一个color颜色来表示遮罩效果。
屏幕截图 2024-04-08 204110

在地图编辑器中,创建N个圆,可以得知圆的半径和【x, y】坐标位置信息。最终给到前端引擎渲染的时候是一个数组列表,因为本身数组就自带了层级信息,所以省去了ZIndex。前端引擎遍历数组列表批量创建精灵节点。那怎么判断要不要给节点加上遮罩颜色呢?用碰撞检测就行,从第一个节点开始,跟后面的节点比较是否发生碰撞。所以前面节点是否被遮挡取决于后面的节点有没有与自己叠加,这样看起来也比较合理,没有与任何节点碰撞就认为是最高层级,可以被点击消除。以下代码简要示意了碰撞逻辑:

for (let e = 0, t = this.flowerNodeList.length; e < t; e++) {
    let node = this.flowerNodeList[e];
    if (node) {
        let pos = v2(node.worldPosition.x, node.worldPosition.y), 
            width = node.getComponent(UITransform).width / 2;
        node.getComponent(Flower).isMask = false;
        for (let a = e + 1; a < t; a++) {
            let node1 = this.flowerNodeList[a];
            if (node1) {
                let pos1 = v2(node1.worldPosition.x, node1.worldPosition.y);
                // 碰撞了就堆叠了
                if (Intersection2D.circleCircle(pos, .88 * width , pos1, .88 * width)) {
                    node.getComponent(Flower).isMask = true; // 设置遮罩
                    break;
                }
            }
        }
    }
}

只要加上这个逻辑就能渲染出带有堆叠效果的地图。生成的地图数量要保证刚好是3的倍速,否则是死局。

在顶层的花朵被点击移除后,重新计算一次碰撞逻辑可以获得最新的顶层节点,直到最后所有的节点被消除。

屏幕截图 2024-04-08 204038

消除的逻辑

有两层,一个是等候区,一个是花盆区。

核心就是花朵的位置变化和消除判断。

如果当前点击花朵的颜色与花盆颜色匹配,则直接将节点添加到花盆上。位置移动之前的游戏也讲过了,就是统一换算到世界坐标系下,不再赘述。如果没有匹配到花盆的颜色,就放入等候区,等候区的数量是有限制的,如果等候区满了,且无法继续打包新的花盆,则游戏失败。花盆上花朵的位置可以放置三个空节点,花盆的目标花色随机从剩余的花池中随机一种。如果当前没有可合成的,玩家可以借助道具。

四种道具

  • 打包花盆:取一个花盆,从花池中取出颜色匹配的花朵,完成打包即可。

  • 清空等待区:这个逻辑需要注意的是等候区的数量不一定是3的倍数,因此需要在花池中补齐剩余的花朵,同时要更新一下消除的进度。

  • 花池随机颜色:获取已有的花池ID列表,乱序,然后重新赋值给每朵花。

  • 添加新花盆,因为本身花盆就是预制体,初始化花盆数量是2个,新增就直接在花盆队列中新增一个,后续花朵会自动被添加到花盆上。

屏幕截图 2024-04-08 204052

其他

以上就是核心的花朵堆叠渲染和消除逻辑,在开发过程中,还遇到一点比较有意思的地方,就是消除花朵解锁花束的进度实现。简单理解就是一个进度条progressbar,理论上是可以用progressbar,但我这次没用,下次专门花点时间讲讲进度条。进度条的纹理要动态变化,不同关卡要解锁的花束不一样。
屏幕截图 2024-04-08 220312

处理方式是两层结构,targetFlower和mask是出于同一层级,然后mask的子节点color是黑色的花束,设置节点color为黑色,遮罩类型为矩形,动态改变遮罩的宽高,targetFlower是原色花束,被遮罩节点叠在上面。

targetFlower和color的spriteFrame都设置为花束的纹理,重点是改变矩形遮罩的高度。但是矩形的锚点位置不能是在中间,要么从顶部要不从底部开始裁剪。


var clearPercent = (totalFlowerNodes - visibleFlowerNodes) / totalFlowerNodes;
// 更新锚点的位置
this.maskRect.parent.getComponent(UITransform).anchorY = 0.5 - clearPercent;

然后就是拖尾的实现,如果想简单点,cocos本身就提供了拖尾的组件MotionStreak(拖尾)组件参考,使用非常容易。需要更加精细的粒子效果需要另外开发,或者用骨骼动画。

最后就是花束列表,这里如果列表节点非常多,则会比较卡,比较好的做法是用虚拟列表,开源社区也有比较成熟的组件(https://github.com/gh-kL/cocoscreator-list) 。目前我还没来得及做,后面优化。
屏幕截图 2024-04-08 203516

最后可以体验一下游戏,跟原版细节上有些差异,但可以继续调优。
gh_98e45d972a08_258

欢迎关注我的公众号【入门游戏开发】,获取更多游戏开发知识和游戏源码,手把手教你做游戏。

每一关的关卡图案都是手动配置的吗

地图都是提前生成好的

1赞

请问地图是用什么工具生成的

牛逼plus

一般都是自己寫的編輯器