最近微博上曝出了很多瓜,"合成大西瓜"这个游戏也很火热,玩了一阵还挺有意思的。研究了一下原理,发现目前流传的版本都是魔改编译后的版本,代码经过压缩不具备可读性,因此决定自己照着实现一个。

本项目主要用作cocos creator练手使用,所有美术素材和音频材料均来源于 www.wesane.com/game/654/。

感谢原作者,向每一位游戏开发者致敬!

本文所有代码及素材都放在github上面了,

https://github.com/tangxiangmin/cocos-big-watermelon

也可以通过在线预览地址体验

https://web-game-9gh6nrus14fec37e-1252170212.tcloudbaseapp.com/

“我自己是一名从事了6年web前端开发的老程序员(我的微信:webxxq),今年年初我花了一个月整理了一份最适合2021年自学的web前端全套培训教程(视频+源码+笔记+项目实战),从最基础的HTML+CSS+JS到移动端HTML5以及各种框架和新技术都有整理,打包给每一位前端小伙伴,这里是前端学习者聚集地,欢迎初学和进阶中的小伙伴(所有前端教程关注我的微信公众号:web前端学习圈,关注后回复“web”即可领取)。

游戏逻辑

整个游戏逻辑比较简单,结合了俄罗斯方块与消除游戏的核心玩法

  • 在生成一个水果

  • 点击屏幕,水果移动到对应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/。

首先访问游戏网站,打开network面板,可以看见游戏依赖的所有美术资源,我们下载自己所需的文件即可

所需的图片资源包括

  • 11张水果贴图

  • 每种水果合成效果贴图,均包含

    • 一张果粒图片

    • 一张圆形水珠图片

    • 一张爆炸贴图

  • 两个西瓜合成时有灯光和撒花的效果,时间有限暂不实现

音频文件同理,可以在Filter栏选择.mp3后缀的请求快速筛选对应资源。

  • 水果消除时的爆炸声和水声

创建游戏场景和背景

打开cocos creator,新建一个项目(也可以直接导入从github下载的项目源码)。

然后记得将刚才下载的素材资源拖拽到右下角的资源管理器中。

创建scene和背景节点

项目初始化之后,在左下角资源管理器新建一个游戏Scene,取名game作为游戏主场景

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

选择game场景,在左上角的层级管理器中可以看见场景的Canvas画布根节点,cocos默认画布是横屏的960*640,可以选择根节点然后再右侧属性检查器中调整宽高为640*960

接下来创建背景层,我们在Canvas节点下面新建一个background节点,由于整个背景是纯色#FBE79D的,因此使用一个单色Sprite填充即可

同样将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,添加一个属性fruitPrefab,其类型为cc.Prefab

// Game.js
properties: {fruitPrefab: {default: null,type: cc.Prefab},
}

回到creator,。选择Canvas节点,可以在属性检查器中的Game组件栏目看见和修改该属性了。我们将刚才制作的prefab资源从资源管理器拖动到这里,在初始化的时候,有cocos负责初始化对应的属性数据

创建单个水果

回到Game.js,开始编写真正的逻辑:创建一个葡萄

// 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种水果(当然也可以添加或者改成其他的东西),如果每种水果都像上面去手动生成预制资源然后分别初始化,那也太繁琐了,我们需要解决动态渲染多种水果的方式。

我们需要获得每种水果的贴图信息,然后在实例化水果时选择对应贴图即可,最简单的方式就是维护一个配置表,每行的数据字段包括idiconSF

const FruitItem = cc.Class({name: 'FruitItem',properties: {id: 0, // 水果的类型iconSF: cc.SpriteFrame // 贴图资源}
});

然后为Game脚本组件新增一个fruits属性,用于保存每种水果的配置信息,其类型是数组,数组内元素类型为刚才创建的FruitItem

// Game.js
properties: {fruits: {default: [],type: FruitItem},
}

回到编辑器,这时候可以发现Game组件的属性下面多了一个Fruits属性,将其长度修改为11,然后依次编写每个水果的id,同时将其贴图资源从资源编辑器贴过来(体力活)

这样我们只需要传入想要制作的水果id,就可以获取到对应的配置信息,并动态修改贴图了

这种初始化的逻辑应该由水果自己维护,因此放在刚才创建的Fruit组件中,我们暴露一个init接口出来

// 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提供了各种事件监听,前端和客户端同学一定不会陌生。

整个游戏会在点击屏幕时创建一个水果,这只要监听一下全局点击事件即可,这个逻辑同样放在Game脚本组件中

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,然后添加物理组件

首先是Rigid Body(刚体)组件

然后是物理碰撞组件,因为我们的水果全是圆形的,都选择PhysicsCircleCollider组件就可以了,如果有个香蕉之类不规则多边形边的话,工作量就会增加不少~

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

完美!!

水果碰撞回调

添加完成之后,还需要开启刚体组件的碰撞属性Enabled Contact Listener,这样可以接收到碰撞之后的回调

这个碰撞回调同样写在Fruit脚本组件里面,

// 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});}}
},

为了保证Fruit组件功能的单一性,在两个相同水果发生碰撞时,我们通过事件通知Game.js,这样可以在初始化水果的时候注册sameContact自定义事件的处理方法

// 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').idconst nextId = id + 1
const newFruit = this.createFruitOnPos(x, y, nextId) // 在指定位置生成新的水果

虽然看起来有点奇怪,但的确可以以玩了!

分析动画

打开源站,通过Performance面板分析一下动画效果(这里就不录gif了)

可以看见合成的时候动画效果包括

  • 碰撞水果向原水果中心移动

  • 果粒爆炸的粒子效果

  • 水珠爆炸的粒子效果

  • 一滩果汁的缩放动画

此外还有爆炸声和水声的音效

管理爆炸素材资源

由于整个动画涉及到的素材较多,每种水果均包含3种颜色不同的贴图,与上面FruitItem类似,我们也采用prefab加动态资源的做法来管理对应素材和动画逻辑。

首先定义一个JuiceItem,保存单种水果爆炸需要的素材

// Game.js
const JuiceItem = cc.Class({name: 'JuiceItem',properties: {particle: cc.SpriteFrame, // 果粒circle: cc.SpriteFrame, // 水珠slash: cc.SpriteFrame, // 果汁}
});

然后为Game组件新增一个juices属性

// Game.js
properties: {juices: {default: [],type: JuiceItem},juicePrefab: {default: null,type: cc.Prefab},
}

接下来又是卖劳力的时候了,将贴图资源都拖放到juices属性下

然后新增一个空的预制资源,主要是为了挂载脚本组件,也就是下面的Juice脚本,然后记得将该预制资源挂载到Game的juicePrefab上。

最后,新建Juice组件,用来实现爆炸的动画逻辑,同样需要暴露init接口

// 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.particlethis.circle = data.particlethis.slash = data.slash},// 动画效果showJuice(){}
}

这样,在合并的时候,我们初始化一个Juice节点,同时展示爆炸效果即可

// 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) // 对应的爆炸逻辑

爆炸粒子动画

关于粒子动画,网上能查到不少资料,如果感兴趣,也可以移步我之前整理的前端常见动画实现原理。

粒子动画的主要的实现思路为:初始化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缩放和透明动画即可
},

源项目的代码中使用createFruitL这个方法来处理爆炸动画,虽然经过了代码压缩,但依稀能看出对应的动画参数逻辑,如果不想调整动画参数,可以借鉴一下

这样,就完成了爆炸效果的展示,大概类似于这样,虽然有点丑

音效

通过cc.audioEngine直接播放AudioClip资源来实现音效

在Game组件下新增两个类型为AudioClip的资源,方便脚本组件访问

properties: {boomAudio: {default: null,type: cc.AudioClip},waterAudio: {default: null,type: cc.AudioClip}
}

同上,在属性检查器中将两个音频资源从资源管理器拖动到Game组件的属性下方

onSameFruitContact(){cc.audioEngine.play(this.boomAudio, false, 1);cc.audioEngine.play(this.waterAudio, false, 1);
}

这样就可以在碰撞的时候听到声音了。

构建打包

完成整个游戏的开发之后,可以选择构建发布,打包成web-mobile版本,然后部署在服务器上,就可以给其他人快乐地玩耍了

小结

不知不就就写到了最后,貌似!!已经大工告成了!!

虽然还有很多细节没有实现,比如添加得分、合成西瓜之后的撒花等功能,感兴趣的同学可以自己克隆去尝试修改一下。本文所有代码及素材都放在github上面了,也可以通过在线预览地址体验

完成这个游戏花了这周六下午 + 一个晚上的时间,由于对cocos creator并不是很熟悉,因此花了一些时间去看文档、查资料,甚至去B站上看了点教学视频。不过收获的成就感与满足感还是很大的,也算是正儿八经写了点游戏。

最后,尤其要感谢我媳妇,帮忙测试及提新需求。不说了,我还得再去加一个点击水果直接消除的功能!

前端项目:从0开始实现一个合成大西瓜相关推荐

  1. 从0开始实现一个合成大西瓜

    作者:橙红年代 (https://juejin.cn/post/6923803717808422925) 最近微博上曝出了很多瓜,"合成大西瓜"这个游戏也很火热,玩了一阵还挺有意思 ...

  2. 为了女朋友!熬夜撸了一个“合成大西瓜”!(附源码)

    本项目主要用作 cocos creator 练手使用,所有美术素材和音频材料均来源于 游戏逻辑 整个游戏逻辑比较简单,结合了俄罗斯方块与消除游戏的核心玩法 在生成一个水果 点击屏幕,水果移动到对应x轴 ...

  3. 撸了一个「合成大西瓜」

    最近「合成大西瓜」这个游戏也很火热,玩了一阵还挺有意思的.研究了一下原理,发现目前流传的版本都是魔改编译后的版本,代码经过压缩不具备可读性,因此决定自己照着实现一个.这个游戏已经开源,获取项目可以关注 ...

  4. 人生重开模拟/合成大西瓜/小空调......2021爆款开源项目盘点

    转眼间,2021年已过大半,你还记得今年在开发者朋友圈里爆红的开源项目吗? 本文就来简要盘点2021涌现出的"宝藏"级开源项目,看看这一届的开发者们,又有哪些新脑洞! 彩蛋:大部分 ...

  5. 魔改《合成大西瓜》——附试玩链接

    最近合成大西瓜这个游戏很火,最近几天都看到朋友圈有很多小伙伴在挑战合成大西瓜. 但是因为合成大西瓜的难度太大了,极少概率能合成大西瓜,无奈只能刷到高分数. 这篇文章就带大家合成一个大西瓜,顺便魔改一下 ...

  6. 魔性“合成大西瓜”背后,我用 350 行代码解开了碰撞之谜!

    [CSDN 编者按]高中物理最烦的几个题:碰撞.守恒.弹性.摩擦--今天全赶一块了.以合成大西瓜为代表的小球碰撞类游戏好玩是好玩,就是有点费程序员.本文,我们将利用简单的 JavaScript 物理引 ...

  7. 魔改和上线你的合成大西瓜,最全教程!

    本文是从 0 到 1 的教程,让小白也能够魔改和上线发布属于你的合成大西瓜! 最近,一款名为『 合成大西瓜 』的游戏突然火了!看来真的是大家吃瓜吃太多了,这个小游戏深抓人心! 当然,游戏本身非常有趣, ...

  8. 合成大西瓜之抱歉~ 我只有大西瓜和刀剑神域~!!

    合成大西瓜之抱歉~ 我只有大西瓜和刀剑神域~!! 文章首发于公众号 Java4ye 链接地址 :合成大西瓜之抱歉~ 我只有大西瓜和刀剑神域~!! 当当当当~ 吃瓜嘞~ 吃瓜嘞~ 先给小伙伴们看看这个效 ...

  9. 西瓜大作战java 源码_魔改和上线你的合成大西瓜,最全教程!

    本文是从 0 到 1 的教程,让小白也能够魔改和上线发布属于你的合成大西瓜! 最近,一款名为『 合成大西瓜 』的游戏突然火了!看来真的是大家吃瓜吃太多了,这个小游戏深抓人心! 当然,游戏本身非常有趣, ...

最新文章

  1. 按AI顶会评实力:美国7倍领先中国,谷歌雄霸全球第一,腾讯和清华分获中国产学No.1...
  2. 软路试--就像一棵树活着
  3. 测试人员:如何品味软件的品位
  4. 搞不懂SDN和SD-WAN?那是因为你没看这个小故事—Vecloud微云
  5. char* 大小_SQL Server中char, nchar, varchar和nvarchar数据类型有何区别
  6. 吴恩达 coursera ML 第十三课总结+作业答案
  7. Java学习_int和Integer的区别+包装类
  8. 数组排序(选择排序和冒泡排序)
  9. 移动端apm关键指标_3个经常被忽视的APM关键功能
  10. 计算机c语言二级试题及答案,计算机c语言二级考试试题及其答案.doc
  11. ES6入门——类的概念
  12. Linux下Mail命令收集
  13. 顺丰同城通过港交所聆讯 今年前五个月收入为30.46亿元
  14. 【Flink】Flink RecordWriterOutput pushToRecordWriter InterruptedException
  15. Java编程框架——Spring Cloud微服务架构实践经验总结
  16. Excel 6位数字与MD5对照表 100000-999999
  17. 如何解读Linux Kernel OOPS信息
  18. 三相桥式全控整流电路simulink仿真_变频器为什么要整流?整流的原理是什么?...
  19. 如何开发Android安卓RFID读卡APP
  20. (超详细)MapReduce工作原理及基础编程

热门文章

  1. 手机通过笔记本上网~
  2. pyspark 数据框 更改大小写_英文字母大小写
  3. c++之防卫式声明和模板
  4. 小型的代码管理仓库Gitea安装指南
  5. Android使用MediaStore.ACTION_IMAGE_CAPTURE拍照
  6. 江苏专转本计算机第七章,2013江苏专转本 第七章 无穷级数.ppt
  7. matlab图片投稿,投稿时图片DPI的设置及相关心得
  8. 解决 “ImportError: attempted relative import with no known parent package“ 问题
  9. 关于学习软件逆向分析意义的阐述
  10. 2. linux安装(1)