图片来自Pexels。研究原理后发现目前流传的版本是魔改后的版本。代码是压缩的,不可读,所以决定自己实现一个。本项目主要用于cocoscreator实践。所有美术素材和音频素材均来自:www.wesane.com/game/654/感谢原作者,向每一位游戏开发者致敬!本文所有代码和资料都放在Github上:https://github.com/tangxiangmin/cocos-big-watermelon也可以通过在线预览地址:https://web-game-9gh6nrus14fec37e-1252170212进行体验。tcloudbaseapp.com/游戏逻辑整个游戏逻辑比较简单,结合了俄罗斯方块和消除类游戏的核心玩法:当生成一个水果,点击屏幕,水果移动到对应的x轴位置,自由落下。每个水果都会与其他水果发生碰撞,两个相同的水果在碰撞时会合并。升级到更高级别的水果水果有11种:游戏的目标是合成最高级的水果:一个大西瓜!当累积的水果超过顶部红线时,游戏结束。梳理需要实现的核心逻辑:生成水果掉落和碰撞水果消除动画效果及升级逻辑准备工作cocoscreator的基本概念整个项目使用cocoscreatorv2.4.3实现。建议刚接触的同学可以先阅读官方文档。这篇文章我不会过多介绍creator的使用(主要是我不是很熟练)。官方文档链接:https://docs.cocos.com/creator/2.3/manual/zh/游戏素材首先需要准备美术资源,本站所有美术素材和音频素材均来自:www.cocos.com/creator/2.3/manual/zh/wesane.com/game/654/首先访问游戏网站,打开网络面板,可以看到游戏所依赖的所有美术资源,我们可以下载我们需要的文件。所需图片资源包括:11张水果贴图。每个水果合成效果贴图包括:果粒图、圆形水滴图、爆炸贴图。合成时有灯光和花朵的两个西瓜由于时间有限,暂时不实现音频文件。同样,您可以在过滤栏中选择后缀为.mp3的请求,快速过滤对应的资源。水果消除时的爆炸声和流水声,音频文件同理,可以在Filter栏选择.mp3后缀的request,快速过滤对应资源创建游戏场景和背景打开cocoscreator,创建新建一个项目(也可以直接导入从github上下载的项目源码):https://github.com/tangxiangmin/cocos-big-watermelon然后记得把刚刚下载的素材资源拖放到资源管理器中在右下角。创建场景并初始化后台节点项目后,在左下角的资源管理器中新建一个游戏场景,并将其命名为游戏作为游戏主场景。创建完成后,在资源管理器的assets中可以看到刚才创建的名为game的场景。选择游戏场景,在左上角的层级管理器中可以看到该场景的Canvas根节点。cocos默认画布是960*640横屏。可以选中根节点,在右边的属性检查器中将宽高调整为640*960。接下来创建背景层。我们在Canvas节点下新建一个背景节点。由于整个背景都是纯色#FBE79D,所以可以用单一颜色的精灵填充。同时将背景节点的宽高调整为整个画布的大小。由于默认锚点为0.5*0.5,因此整个画布将被完全填满。现在整个游戏场景是这样的:接下来,设计游戏的逻辑脚本部分。场景脚本组件在assets目录下新建一个js脚本,按照惯例命令为Game.js,创建者会生成一个模板文件,里面有基本的cc.Class。先将脚本组件与节点关联起来,选择Canvas根节点,在右侧的属性检查器中添加组件,然后选择刚刚创建的Game组件。然后编写具体的代码逻辑,打开Game.js文件(推荐使用vscode或者webstrom打开整个项目的根目录进行编辑)。里面的初始代码是这样的://Game.jscc.Class({extends:cc.Component,properties:{},onLoad(){},start(){}})我们需要维护整个的逻辑游戏在这里,后面一步步添加代码内容。创造水果水果是整个游戏的核心元素,在游戏中经常被创造和摧毁。生成单个水果预制资源。这个动态创建的节点可以通过预制资源Prefab来控制。制作Prefab最简单的方法是将资源从资源管理器中拖到场景编辑器中,然后将层级管理器中的节点拖回资源管理器中。这里我们以最底层的水果“葡萄”为例:然后删除层级管理器中的节点,这样我们就得到了一个水果预制资源。在脚本组件中,我们可以使用代码通过预制资源动态生成节点。修改Game.js,添加一个属性fruitPrefab,类型为cc.Prefab://Game.jsproperties:{fruitPrefab:{default:null,type:cc.Prefab},}回到creator,选择Canvas节点,您可以选择属性检查器中的游戏组件列查看并修改此属性。我们从资源管理器中把刚刚制作好的预制资源拖到这里。初始化时,cocos负责初始化对应的属性数据:创建单个水果,返回Game.js开始编写真正的逻辑:创建葡萄。//Game.jsonLoad(){letfruit=cc.instantiate(this.fruitPrefab);fruit.setPosition(cc.v2(0,400));this.node.addChild(fruit);}在预览模式下,可以看到屏幕上方有一颗葡萄:nice,很好的开始!另外,由于fruit中还包含了一些特定的逻辑,我们可以给它添加一个Fruit脚本组件,虽然目前看来用处不大!创建一个Fruit脚本组件和上面创建一个Game组件类似,然后再次选择刚才制作的prefab进行编辑,将其关联到Fruit用户脚本组件。动态保持各种水果。整个游戏共有11种水果(当然你也可以添加或改成其他东西)。如果每个水果都像上面那样手动生成并分别初始化,那就太繁琐了。我们需要解决动态如何渲染多个水果。我们需要获取每个水果的纹理信息,然后在实例化水果的时候选择对应的纹理。最简单的方法就是维护一张配置表,每一行的数据字段包括id和iconSF。constFruitItem=cc.Class({name:'FruitItem',properties:{id:0,//水果图标类型SF:cc.SpriteFrame//地图资源}});然后为Game脚本组件添加一个fruits属性,用来保存每个水果的配置信息。它的类型是一个数组,数组中的元素类型是刚刚创建的FruitItem。//Game.jsproperties:{fruits:{default:[],type:FruitItem},}回到编辑器,可以发现Game组件的属性下多了一个Fruits属性,修改其长度为11,然后写下每个水果的id,同时从资源编辑器中粘贴它的贴图资源(手工)。这样我们只需要传入我们要制作的水果的id,就可以获取到相应的配置信息,动态修改贴图。这种初始化逻辑应该是fruit自己维护的,所以在刚刚创建的Fruit组件中,我们暴露了一个init接口。//Fruit.jsproperties:{id:0,},//实例可以放在其他组件调用init(data){this.id=data.id//根据constsp传入的参数修改纹理资源=这个。node.getComponent(cc.Sprite)sp.spriteFrame=data.iconSF},然后修改上面的初始化水果代码://Game.jscreateOneFruit(num){letfruit=cc.instantiate(this.fruitPrefab);//GetGo到配置信息constconfig=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,里面展示的比较详细(至少比文档更容易理解)。要启用物理引擎和碰撞检测,第一步是启用物理引擎并设置重力:constinstance=cc.director.getPhysicsManager()instance.enabled=true//instance.debugDrawFlags=4instance.gravity=cc.v2(0,-960);然后需要开启碰撞检测,默认是关闭的:constcollisionManager=cc.director.getCollisionManager();collisionManager.enabled=true然后设置碰撞的四周墙,这样水果就会不跌倒没有限制://设置周围碰撞区域letwidth=this.node.width;letheight=this.node.height;letnode=newcc.Node();letbody=node.addComponent(cc.RigidBody);body.type=cc.RigidBodyType.Static;const_addBound=(node,x,y,width,height)=>{letcollider=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;现在我们已经打开了游戏世界的物理引擎,接下来我们需要配置它受引擎影响的节点就是我们的果实。FruitRigidBodyComponentsandCollisionComponents回到creator,找到我们的fruitprefab,添加物理组件。首先是RigidBody(刚体)组件:然后是物理碰撞组件,因为我们的水果都是圆形的,所以可以选择PhysicsCircleCollider组件。如果有香蕉等不规则的多边形边,工作量会增加。挺多的~接下来可以看看整体效果(记得加上刚才的点击事件,然后控制随机水果类型):完美!!添加水果碰撞回调后,还需要开启刚体组件EnabledContactListener的碰撞属性,可以接收到碰撞后的回调。Fruit脚本组件中也写了这个碰撞回调://Fruit.jsonBeginContact(contact,self,other){//检测到两个相同的水果碰撞if(self.node&&other.node){consts=self.node.getComponent('Fruit')consto=other.node.getComponent('Fruit')if(s&&o&&s.id===o.id){self.node.emit('sameContact',{self,other});}}},为了保证Fruit组件功能的单一性,当两个相同的水果发生碰撞时,我们通过一个事件通知Game.js,这样我们就可以在初始化水果的时候注册相同的Contact自定义事件处理方法。//Game.jscreateOneFruit(num){letfruit=cc.instantiate(this.fruitPrefab);//...其他初始化逻辑fruit.on('sameContact',({self,other})=>{//二节点会被触发,临时处理,看看有没有其他方法只显示一次other.node.off('sameContact')//处理水果合并的逻辑,再处理this.onSameFruitContact({self,other})})}这样,当水果发生碰撞时,我们可以监控并处理消除升级逻辑。消除水果动画不带动画简单的消除逻辑就是删除两个节点,然后在原来的水果位置生成一个更高级的水果,没有任何动画效果:self.node.removeFromParent(false)other.node.removeFromParent(false)const{x,y}=other.node//获取合并后的水果位置constid=other.getComponent('Fruit').idconstnextId=id+1constnewFruit=this.createFruitOnPos(x,y,nextId)//在指定位置position虽然生成新水果看起来有点奇怪,但确实是可以玩的!动画分析打开源码站点,通过Performance面板分析动画效果(这里不记录gif)。可以看到合成时的动画效果有:碰撞水果向原水果中心移动水果爆炸粒子效果水滴爆炸粒子效果一滩果汁缩放动画此外还有爆炸和水的音效。管理爆炸材质资源由于整个动画涉及的材质较多,每个水果包含3个不同颜色的贴图。和上面的FruitItem类似,我们也是使用prefab加上动态资源来管理相应的材质和动画逻辑。首先定义一个JuiceItem来保存单个水果爆炸需要的素材://Game.jsconstJuiceItem=cc.Class({name:'JuiceItem',properties:{particle:cc.SpriteFrame,//水果圈:cc.SpriteFrame,//水珠斜杠:cc.SpriteFrame,//果汁}});然后为Game组件添加一个juices属性://Game.jsproperties:{juices:{default:[],type:JuiceItem},juicePrefab:{default:null,type:cc.Prefab},}接下来,是时候再次努力。拖拽juices属性下的texture资源:然后添加一个空的prefab资源,主要用于挂载脚本组件。也就是下面的Juice脚本,然后记得把预制资源挂载到Game的juicePrefab上。最后新建一个Juice组件来实现爆炸动画逻辑,同样需要暴露init接口://Juice.jscc.Class({extends:cc.Component,properties:{particle:{default:null,type:cc.SpriteFrame},circle:{default:null,type:cc.SpriteFrame},slash:{default:null,type:cc.SpriteFrame}},//同时暴露一个初始化接口init(data){this.particle=data.particlethis.circle=data.particlethis.slash=data.slash},//动画效果showJuice(){}}这样合并的时候我们初始化一个Juice节点,同时显示爆炸效果。//Game.jsletjuice=cc.instantiate(this.juicePrefab);this.node.addChild(juice);constconfig=this.juices[id-1]constinstance=juice.getComponent('Juice')instance.init(config)实例.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){//fruitfor(leti=0;i<10;++i){constnode=newcc.Node('Sprite');constsp=node.addComponent(cc.Sprite);sp.spriteFrame=this.particle;node.parent=this.node;//...一堆随机参数node.position=pos;node.runAction(cc.sequence(//...各种动作对应的动画逻辑cc.callFunc(function(){//消除粒子node.active=false},this)))}//水滴for(letf=0;f<20;f++){//同样的水果粒子,使用的spriteFrame是switchedtothis.circle}//Juice只有一张贴图,使用this.slash展示常规动作缩放和透明动画},源项目代码使用createFruitL方法处理爆炸动画,虽然经过压缩,但是依稀可以看出对应动画参数的逻辑。如果不想调整动画参数,可以借鉴一下。这样就完成了爆炸效果的展示,和这个大致差不多,虽然丑了点。音效是通过cc.audioEngine直接播放AudioClip资源实现的。在Game组件下添加两个AudioClip类型的资源,方便脚本组件访问:properties:{boomAudio:{default:null,type:cc.AudioClip},waterAudio:{default:null,type:cc.AudioClip}}同上,从资源管理器中拖拽两个音频资源到属性检查器中Game组件的属性中:,1);}以便在发生碰撞时可以听到声音。构建打包完成整个游戏的开发后,可以选择构建发布,打包成网页版-手游版,然后部署到服务器上,让其他人玩的开心。摘要不知不觉就写到了最后。看来工作已经完成了!!虽然还有很多细节没有实现,比如加点,合成西瓜后的洒水器,有兴趣的同学可以自己clone,尝试修改。.本文所有代码和资料都放在Github上,大家也可以通过在线预览地址进行体验:https://github.com/tangxiangmin/cocos-big-watermelonhttps://web-game-9gh6nrus14fec37e-1252170212.tcloudbaseapp.com/花了这个星期六下午+一晚上的时间来完成这个游戏。由于对cocoscreator不是很熟悉,所以花了一些时间看文档,查资料,甚至在哔哩哔哩上看了一些教学视频。不过获得的成就感和满足感还是很大的,也算是认真的游戏了。最后,我要特别感谢我的妻子,她帮助测试和提出新的需求。还别说,还得再加一个点击水果直接消除的功能!作者:shymean编辑:陶家龙来源:www.shymean.com/article/用cocos实现一个合成大西瓜
