使用 cocos 实现一个合成大西瓜

671 阅读4分钟

参考链接

cocos v2.4 文档

使用cocos实现一个合成大西瓜

序言

做一个合成大西瓜给对象送礼物(以上面的参考代码为例,直接拿过来发现埋有诸多bug,只做思路上的参考,绝大部分代码都需要自己写,GitHub上的大部分代码都是 cocos 编译后的,代码不可读,因此不可用)。且参考的项目只有基础的功能,在这次活动中式不够用的。下面将整理一下本次活动踩的坑,有兴趣的同学可以参考一下,有疑问的同学可以留言或者私信我。

看这个文章有什么用

可以去魔改属于自己的合成大西瓜

cocos 版本

cocos creator 2.4.8

需要实现的功能

基本功能

两个相同的球碰撞合成新的大一个级别的球,播放合成音效,加分。最大的球碰撞不合成新的球,不加分。累计的球超过线游戏结束。基本功能在参考链接里面基本都有,主要工作就是排雷。

彩蛋功能

同一次操作(第n个球碰撞道其他物品开始计时,第n+1个球碰撞道物体结束计时)合成3个或者以上的球触发彩蛋,彩蛋为一个带有音效的动画。

积分面板

计算分数

游戏结束提示面板

游戏结束时候引导用户去分享页面,或者在玩一次

遇到的问题

IOS 首次播放音效时候有明显的延时

相同的球碰撞会合成新的球,这个时候需要播放合成音效,IOS 端首次播放声音有明显的延时,之后声音播放都没有延迟。而安卓是没有该问题的。

分析

推测可能是首次播放音效需要去开启 IOS 的音效权限什么造成的延迟播放。

解决方案

游戏场景加载首次线播发一下静音的短音频。当然这个可能不是最佳方案,但是却可以完美的解决这个问题,这个短音频没有声音,体积也很小,也不会对用户造成干扰。

一级球落下同时砸到2个一级球,同时合成了2个二球。

正常情况应该是碰撞之后只能合成1个二级别球,另一个一级别球不受影响。

分析

球节点碰撞以后会触发 cocos 的碰撞回调函数,如果是相同的球碰撞,就去移除监听合成事件,删除节点,合成新的节点。这里面经过了 cocos 的 emit 事件,回调里面移除了该事件,可能是 cocos 提供的 emit 事件是异步的,导致元素没有及时删除,造成多次触发合成事件导致。(实际最终代码我也把'sameContact'放到了下个事件队列,这个涉及到彩蛋控制的问题,去掉这个setTimeout并不影响在下一个任务队列触发。)

解决方法

相同节点碰撞触发 emit 之前先标记节点,给一个删除表示标识。

self.node.isRemove = true;
other.node.isRemove = true;
setTimeout(() => { // 需要确保lastOneCrash先触发,把下面的任务放在下一个宏队列
    self.node.emit('sameContact', {self, other});
},0)

控制生成彩蛋

参考上面的彩蛋功能

分析

  1. 这个功能难点在于如何如何界定同一个操作的结束时间。起初用的方案是第n个球的结束时间为第n+1个球开始下落的时间。但是发现这么做问题比较大,因为从顶部下落到地步的时间大约需要0.5s, 这么长的时间内很可能上一个球造成的滚动效应没有消失从而又合成了新的球。又想到是否可以下落后0.5s后再计算时间呢?实际上也不可行,因为如果球有堆积的化,下落时间就回不等于0.5s, 而且0.5s不是准确值。
  2. 第 n 个球落下碰撞的时候统计清零,直到第 n+1 个球落下即可判断期间合成的球的个数。

解决方案

// Fruit.js 
if (s && s.sign && self.node.isGetSign !== true) {
    self.node.isGetSign = true;
    self.node.emit('lastOneCrash', s.sign);
}

// Game.js

fruit.on('lastOneCrash', this.onLastOneCrash.bind(this))
onLastOneCrash(sign) {
    if (!sign) {
        return;
    }
    this.mergeCount = 0;
},

sign 为一个标示,就是只有自己创建的福利元素才给的这个标示。 就是福利元素碰撞时候,如果 isGetSign 不为真说明第一次碰撞,可以进入该元素的周期,把之前合成的个数清零。

制作彩蛋动画

  1. 创建一个节点用来挂载动画:

01.png

  1. 给节点添加一个 animation 组件:

02.png

  1. 创建 clip 文件:

03.png 4. 编辑动画;

04.png 5. 添加一个 cc.sprite.spriteFrame 属性: 05.png

  1. 把帧动画图片拖到 cc.sprite.spriteFrame 的动画区域就可以形成一个动画帧:

06.png

  1. sample 和 speed 两个属性可以调节动画播放的速率,调节到合适的速度即可: 07.png

  2. 代码中引入:

cc.Class({
    animationTest: {
        default: null,
        type: cc.Animation
    }
})
  1. 把动画和场景关联起来:

08.png 10. 播放动画:

this.animationTest.play()

结语

七夕马上到了,快去开发一个你的合成大西瓜给你的另一半合成礼物吧