为了媳妇,熬夜撸了一个合成大西瓜!
最近微博上曝出了很多瓜,"合成大西瓜"这个游戏也很火热,玩了一阵还挺有意思的。

图片来自 Pexels
研究了一下原理,发现目前流传的版本都是魔改编译后的版本,代码经过压缩不具备可读性,因此决定自己照着实现一个。
www.wesane.com/game/654/
感谢原作者,向每一位游戏开发者致敬!
本文所有代码及素材都放在 Github 上:
https://github.com/tangxiangmin/cocos-big-watermelon
https://web-game-9gh6nrus14fec37e-1252170212.tcloudbaseapp.com/
游戏逻辑
整个游戏逻辑比较简单,结合了俄罗斯方块与消除游戏的核心玩法:
在生成一个水果
点击屏幕,水果移动到对应 x 轴位置并自由下落
每个水果会与其他水果发生碰撞,两个相同的水果碰撞时会发生合并,升级成更高一级的水果
水果共有 11 种类型:

游戏目标是合成最高级的水果:大西瓜!当堆积的水果超过顶部红线时则游戏结束。
整理出需要实现的核心逻辑:
生成水果
水果下落与碰撞
水果消除动画效果及升级逻辑
预备工作
cocos creator 基本概念
整个项目使用 cocos creator v2.4.3 实现,建议初次了解的同学可以先过一下官方文档,本文不会过多介绍 creator 的使用(主要是我也不太熟练 hah)。
https://docs.cocos.com/creator/2.3/manual/zh/
游戏素材
首先需要准备美术资源,本位所有美术素材和音频材料均来源于:
www.wesane.com/game/654/

所需的图片资源包括:
11 张水果贴图
每种水果合成效果贴图,均包含:一张果粒图片,一张圆形水珠图片,一张爆炸贴图
两个西瓜合成时有灯光和撒花的效果,时间有限暂不实现
音频文件同理,可以在 Filter 栏选择 .mp3 后缀的请求快速筛选对应资源。水果消除时的爆炸声和水声,音频文件同理,可以在 Filter 栏选择 .mp3 后缀的请求快速筛选对应资源
创建游戏场景和背景
https://github.com/tangxiangmin/cocos-big-watermelon
然后记得将刚才下载的素材资源拖拽到右下角的资源管理器中。
创建 scene 和背景节点
项目初始化之后,在左下角资源管理器新建一个游戏 Scene,取名 game 作为游戏主场景。

创建完毕后就可以在资源管理器的 assets 中看见刚才创建的名为 game 的 scene。


同样将 background 节点宽高调整为整个画布的大小,由于默认锚点均为 0.5*0.5,此时整个画布会被完全填充。
现在整个游戏场景大概是这个样子的:

接下来设计游戏的逻辑脚本部分。
场景脚本组件
在 assets 目录下新建一个 js 脚本,按照惯例命令成 Game.js,creator 会生成一个带基础 cc.Class 的模板文件。

先将脚本组件与节点关联起来,选择 Canvas 根节点,在右侧属性检查器中添加组件,然后选择刚才创建的这个 Game 组件。

然后编写具体的代码逻辑,打开 Game.js 文件(建议使用 vscode 或者 webstrom 打开整个项目的根目录进行编辑)。
//?Game.js
cc.Class({
????extends:?cc.Component,
????properties:?{
????},
????onLoad(){
????},
????start(){?}
})
我们需要在这里维护整个游戏的逻辑,后面逐步添加代码内容。
创建水果
水果是整个游戏的核心元素,在游戏中被频繁创建和销毁。
生成单个水果预制资源
这种动态创建的节点可以通过预制资源 Prefab 来控制,制作 Prefab 最简单的方式就是将资源从资源管理器拖动到场景编辑器中,然后再将层级管理器中的节点拖回资源管理器。
这里以等级最低的水果“葡萄”为例:


然后将层级管理器中的节点删除,这样我们就得到了一个 fruit 的预制资源,在脚本组件中,就可以使用代码通过预制资源动态生成节点了。
//?Game.js
properties:?{
????fruitPrefab:?{
????????default:?null,
????????type:?cc.Prefab
????},
}
回到 creator,选择 Canvas 节点,可以在属性检查器中的 Game 组件栏目看见和修改该属性了。

创建单个水果
//?Game.js
onLoad(){
????let?fruit?=?cc.instantiate(this.fruitPrefab);
????fruit.setPosition(cc.v2(0,?400));
????this.node.addChild(fruit);
}

Nice,非常好的开始!
此外,由于水果还包含一些特定的逻辑,我们可以向它添加一个 Fruit 脚本组件,虽然目前看起来还没有什么用!
创建 Fruit 脚本组件与上面创建 Game 组件类似,然后选择刚才制作的 prefab 重新编辑,关联上 Fruit 用户脚本组件即可。
动态维护多种水果
整个游戏共 11 种水果(当然也可以添加或者改成其他的东西),如果每种水果都像上面去手动生成预制资源然后分别初始化,那也太繁琐了,我们需要解决动态渲染多种水果的方式。
const?FruitItem?=?cc.Class({
????name:?'FruitItem',
????properties:?{
????????id:?0,?//?水果的类型
????????iconSF:?cc.SpriteFrame?//?贴图资源
????}
});
//?Game.js
properties:?{
????fruits:?{
????????default:?[],
????????type:?FruitItem
????},
}

这样我们只需要传入想要制作的水果 id,就可以获取到对应的配置信息,并动态修改贴图了。
//?Fruit.js
properties:?{
????id:?0,
},
//?实例放在可以在其他组件中调用
init(data)?{
????this.id?=?data.id
????//?根据传入的参数修改贴图资源
????const?sp?=?this.node.getComponent(cc.Sprite)
????sp.spriteFrame?=?data.iconSF
},
//?Game.js
createOneFruit(num)?{
????let?fruit?=?cc.instantiate(this.fruitPrefab);
????//?获取到配置信息
????const?config?=?this.fruits[num?-?1]
????//?获取到节点的Fruit组件并调用实例方法
????fruit.getComponent('Fruit').init({
????????id:?config.id,
????????iconSF:?config.iconSF
????});
}
这样就可以愉快的创建各种水果了。
监听点击事件
cocos 提供了各种事件监听,前端和客户端同学一定不会陌生。
onLoad()?{
????//?监听点击事件
????this.node.on(cc.Node.EventType.TOUCH_START,?this.onTouchStart,?this)
},
onTouchStart(){
????this.createOneFruit(1)?//?生成水果
}
实际游戏中还需要处理随机生成水果、上一个水果在点击的 x 轴下落等细节逻辑,这里不再赘述。
物理系统:自由落体与刚体碰撞
上面处理了水果创建的逻辑,在整个游戏中,水果是可以产生下落及弹性碰撞等物理效果的,利用 cocos 内置的物理引擎,可以很方便的实现。
对 cocos 引擎不熟悉的同学可以先看看这个官方 demo,里面展示的比较详细(起码比文档要更容易理解)。
开启物理引擎与碰撞检测
const?instance?=?cc.director.getPhysicsManager()
instance.enabled?=?true
//?instance.debugDrawFlags?=?4
instance.gravity?=?cc.v2(0,?-960);
const?collisionManager?=?cc.director.getCollisionManager();
collisionManager.enabled?=?true
?//?设置四周的碰撞区域
let?width?=?this.node.width;
let?height?=?this.node.height;
let?node?=?new?cc.Node();
let?body?=?node.addComponent(cc.RigidBody);
body.type?=?cc.RigidBodyType.Static;
const?_addBound?=?(node,?x,?y,?width,?height)?=>?{
????let?collider?=?node.addComponent(cc.PhysicsBoxCollider);
????collider.offset.x?=?x;
????collider.offset.y?=?y;
????collider.size.width?=?width;
????collider.size.height?=?height;
}
_addBound(node,?0,?-height?/?2,?width,?1);
_addBound(node,?0,?height?/?2,?width,?1);
_addBound(node,?-width?/?2,?0,?1,?height);
_addBound(node,?width?/?2,?0,?1,?height);
node.parent?=?this.node;
现在我们就开启了游戏世界的物理引擎,然后还需要配置需要受引擎影响的节点,也就是我们的水果。
水果刚体组件与碰撞组件
回到 creator,找到我们的水果 prefab,然后添加物理组件。


接下来可以看看整体效果,(记得把刚才的点击事件加上,然后控制一下随机生成水果类型):

完美!!
水果碰撞回调

//?Fruit.js
onBeginContact(contact,?self,?other)?{
????//?检测到是两个相同水果的碰撞
????if?(self.node?&&?other.node)?{
????????const?s?=?self.node.getComponent('Fruit')
????????const?o?=?other.node.getComponent('Fruit')
????????if?(s?&&?o?&&?s.id?===?o.id)?{
????????????self.node.emit('sameContact',?{self,?other});
????????}
????}
},
//?Game.js
createOneFruit(num)?{
????let?fruit?=?cc.instantiate(this.fruitPrefab);
????//?...其他初始化逻辑
?????fruit.on('sameContact',?({self,?other})?=>?{
????????//?两个node都会触发,临时处理,看看有没有其他方法只展示一次的
????????other.node.off('sameContact')?
????????//?处理水果合并的逻辑,下面再处理
????????this.onSameFruitContact({self,?other})
?????})
}
这样当水果发生碰撞时,我们就能够监听并处理消除升级逻辑了。
消除水果动画
无动画版本
self.node.removeFromParent(false)
other.node.removeFromParent(false)
const?{x,?y}?=?other.node?//?获取合并的水果位置
const?id?=?other.getComponent('Fruit').id
const?nextId?=?id?+?1
const?newFruit?=?this.createFruitOnPos(x,?y,?nextId)?//?在指定位置生成新的水果
虽然看起来有点奇怪,但的确可以以玩了!
分析动画

可以看见合成的时候动画效果包括:
碰撞水果向原水果中心移动
果粒爆炸的粒子效果
水珠爆炸的粒子效果
一滩果汁的缩放动画
此外还有爆炸声和水声的音效。
管理爆炸素材资源
由于整个动画涉及到的素材较多,每种水果均包含 3 种颜色不同的贴图,与上面 FruitItem 类似,我们也采用 prefab 加动态资源的做法来管理对应素材和动画逻辑。
//?Game.js
const?JuiceItem?=?cc.Class({
????name:?'JuiceItem',
????properties:?{
????????particle:?cc.SpriteFrame,?//?果粒
????????circle:?cc.SpriteFrame,?//?水珠
????????slash:?cc.SpriteFrame,?//?果汁
????}
});
//?Game.js
properties:?{
????juices:?{
????????default:?[],
????????type:?JuiceItem
????},
????juicePrefab:?{
????????default:?null,
????????type:?cc.Prefab
????},
}

然后新增一个空的预制资源,主要是为了挂载脚本组件,也就是下面的 Juice 脚本,然后记得将该预制资源挂载到 Game 的 juicePrefab 上。
//?Juice.js
cc.Class({
????extends:?cc.Component,
????properties:?{
????????particle:?{
????????????default:?null,
????????????type:?cc.SpriteFrame
????????},
????????circle:?{
????????????default:?null,
????????????type:?cc.SpriteFrame
????????},
????????slash:?{
????????????default:?null,
????????????type:?cc.SpriteFrame
????????}
????},
????//?同样暴露一个init接口
????init(data)?{
????????this.particle?=?data.particle
????????this.circle?=?data.particle
????????this.slash?=?data.slash
????},
????//?动画效果
????showJuice(){
????}
}
//?Game.js
let?juice?=?cc.instantiate(this.juicePrefab);
this.node.addChild(juice);
const?config?=?this.juices[id?-?1]
const?instance?=?juice.getComponent('Juice')
instance.init(config)
instance.showJuice(pos,?n)?//?对应的爆炸逻辑
爆炸粒子动画
https://www.shymean.com/article/%E5%89%8D%E7%AB%AF%E5%B8%B8%E8%A7%81%E5%8A%A8%E7%94%BB%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86
粒子动画的主要的实现思路为:初始化 N 个粒子,控制他们的速度大小、方向和生命周期,然后控制每个粒子按照对应的参数执行动画,所有粒子汇集在一起的效果就组成了粒子动画。
showJuice(pos,?width)?{
????//?果粒
????for?(let?i?=?0;?i?<?10;?++i)?{
????????const?node?=?new?cc.Node('Sprite');
????????const?sp?=?node.addComponent(cc.Sprite);
????????sp.spriteFrame?=?this.particle;
????????node.parent?=?this.node;
????????//?...?一堆随机的参数
????????node.position?=?pos;
????????node.runAction(
????????????cc.sequence(
????????????????//?...各种action对应的动画逻辑
????????????????cc.callFunc(function?()?{
????????????????????//?动画结束后消除粒子
????????????????????node.active?=?false
????????????????},?this))
????????)
????}
????//?水珠
????for?(let?f?=?0;?f?<?20;?f++)?{
????????//?同果粒,使用的spriteFrame切换成?this.circle
????}
????//?果汁只有一张贴图,使用this.slash,展示常规的action缩放和透明动画即可
},


音效
通过 cc.audioEngine 直接播放 AudioClip 资源来实现音效。
properties:?{
????boomAudio:?{
????????default:?null,
????????type:?cc.AudioClip
????},
????waterAudio:?{
????????default:?null,
????????type:?cc.AudioClip
????}
}
onSameFruitContact(){
????cc.audioEngine.play(this.boomAudio,?false,?1);
????cc.audioEngine.play(this.waterAudio,?false,?1);
}
这样就可以在碰撞的时候听到声音了。
构建打包

小结
不知不就就写到了最后,貌似!!已经大工告成了!!
虽然还有很多细节没有实现,比如添加得分、合成西瓜之后的撒花等功能,感兴趣的同学可以自己克隆去尝试修改一下。
https://github.com/tangxiangmin/cocos-big-watermelon
https://web-game-9gh6nrus14fec37e-1252170212.tcloudbaseapp.com/
完成这个游戏花了这周六下午+一个晚上的时间,由于对 cocos creator 并不是很熟悉,因此花了一些时间去看文档、查资料,甚至去 B 站上看了点教学视频。不过收获的成就感与满足感还是很大的,也算是正儿八经写了点游戏。
最后,尤其要感谢我媳妇,帮忙测试及提新需求。不说了,我还得再去加一个点击水果直接消除的功能!
如果想获得合成大西瓜鸿蒙版
或者体验魔改版作弊模式
关注公众号:拾黑(shiheibook)了解更多 [广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注网络尖刀微信公众号随时掌握互联网精彩
- 1 中共中央召开党外人士座谈会 7904429
- 2 日本又发生6.6级地震 7807878
- 3 河北沧州杀妻案男方被判死刑 7713357
- 4 全国首艘氢电拖轮作业亮点多 7617376
- 5 王毅:台湾地位已被“七重锁定” 7520466
- 6 经典版QQ宣布回归 7427060
- 7 男子带老婆买糖葫芦被认成父女 7329333
- 8 苏炳添宣布退役 7234456
- 9 日本震中突发大火 民众开车逃命 7137982
- 10 “人造太阳”何以照进现实 7044373







51CTO技术栈
