上一篇搭建好了一个游戏框架。接下来就要用框架做一些游戏了。
2014年,《围住神经猫》火了一把,html5游戏,egret也跟着火了。
其实这个游戏很早就有了,原作者是日本人。最初的版本(我见过的最早的)是用as2写的。通过破解swf文件,得到了一个网址:http://www.gamedesign.jp/这个可能是原作者的网站。(如有错误,欢迎指正)
其实,有好多很好玩的flash小游戏。国外原创的居多,国内大多数都是抄袭的,当然也不乏原创的好作品。
/coolSite.html这里搜集了一些好玩的小游戏,以及一些小游戏网站。也可以直接看这里http://www.zhihu.com/question/21182817。
本来想着用air直接移植到手机上,但是发现好多都是as2时代的作品,air貌似不支持as2,只好作罢。
不说废话了,开始介绍游戏的实现。
由于代码略多,作者本身逻辑也比较混乱,想到哪写到哪,写完自己都忘了怎么做的了。所以不详细解释了,只说说一些比较重要的地方。具体实现可下载源文件来看。
假设你已经下载好了之前的模板。
1)修改index.html。
只需要修改canvas的宽高,以及title。作者把宽高修改成了height="480" width="800",做完后发现手机上不太合适,也不好改了。
2)修改HResources.js。
修改之前,应该已经准备好了图片素材以及声音素材。
3)修改HGame.js。
HGame.data中的变量做了修改。
逻辑和之前的模板略有不同,所以需要稍作修改。游戏结束的时候,有两种情况,一种是游戏失败,一种是游戏成功。游戏成功的话,结束界面应该有一个“继续挑战”按钮,点击之后,直接进入游戏界面,游戏失败,才是之前模板中的逻辑。
结束界面增加了一个侦听overLayer.addEventListener("nextLevel",nextLevel);实现了nextLevel函数。不回到开始界面,而是直接进入下一关。
4)修改HMenuLayer.js、HOverLayer.js。
这两个界面主要就是摆放一些可视元件,侦听按钮点击,发送事件。
可视元件无非是:文本框、位图、矢量图,复杂一点的有按钮、动画。
文本可以用HUtils.createText来实现,自己做了封装;
矢量图用graphics来实现;
位图直接new createjs.Bitmap(HResources.getResourceByTag("hexagon"));
创建按钮用HUtils.createBtn(HResources.getResourceByTag("buttons"),164,54,2,3);
(HUtils中的几个方法,参数具体含义看源文件)
动画用createjs.Sprite和createjs.SpriteSheet来实现,比较复杂,创建猫的时候再细说。
摆放完元件,让按钮侦听点击,点击之后发送相应的事件就完了。比如:
btn.addEventListener("click",clickHandler); function clickHandler(e){ var event = new createjs.Event("complete"); s.dispatchEvent(event);//类最开始有一个定义var s=this; s.hide(); }
修改完可以先预览一下效果,没有错误之后,在往下进行。
3)HGameLayer.js。
重头戏在这里。
对于神经猫游戏界面,主要有3层,最下层是背景,第二层是六边形,第3层是猫。
为了方便调整位置,我们在六边形和猫外边再套一层。
六边形的创建。
可以用位图来实现,也可以用矢量图,我用的位图。
六边形有两个状态,一个状态允许猫站上去,一个状态不允许猫站上去。因此应该有两个不同颜色的六边形图片。我们把图片放在了hexagon.png中。
//创建六边形 function gethHexagon(){ var data = { images: [HResources.getResourceByTag("hexagon")], frames: {width:30, height:30,regX: 0, regY:0}, animations: { normal:0, locked:1 } }; var spriteSheet = new createjs.SpriteSheet(data); var sp=new createjs.Sprite(spriteSheet,"normal"); return sp; }
作者用的createjs.Sprite来创建六边形,createjs.Sprite相当于flash中的MovieClip,可以方便的用gotoAndStop()来跳转到不同的状态。
六边形只有两个状态,比较简单,也可以用自己用Bitmap来实现。
创建并摆放n*n个六边形就不多说了。
六边形的点击。
这个游戏,玩家的唯一操作就是点击六边形。
我们创建了n*n个六边形,如何侦听玩家点击呢。
由于六边形的矩形区域时重叠的,可能会使得侦听点击比较困难。
想到的解决办法:
(1)获取鼠标点击坐标,计算点击坐标在哪个六边形内。方法不太难,不会的话百度一下就有了。作者试了一下,由于用的六边形图片不是规则的六边形,导致精度略差。
(2)像素级别的判断。这里的六边形位置都是固定的,所以实现起来也简单,把六边形draw到一个bitmap上,每个六边形的颜色都不一样(与索引位置相关即可),点击的时候,获取bitmap相应位置的色值,根据色值即可判断点击的是哪个六边形。
最后是这么做的:直接侦听点击,判断target就行了。
这里需要注意:
(1)我们是给六边形的容器侦听点击,而不是六边形。因为容器加侦听,只需要一个就够了,六边形加侦听需要n*n,效率上会差很多。侦听处理里边取e.targe就可以取到六边形了。
(2)我们给六边形加了一个属性,用来存储六边形的二维索引位置。
初始化或者点击之后,切换六边形的状态,只需要gotoAndStop()就可以了。
猫的创建。
猫在逃跑时,有6个方向可以选择,考虑对称,也需要3个动画效果:左、左上、左下。可以看一下cat.png。
var anim01=[[0,0,31,45,0,13.75,35.25],[31,0,41,46,0,23.75,36.25],[72,0,40,63,0,23.75,36.25],[112,0,40,61,0,29.75,28.25],[152,0,40,61,0,29.75,28.25],[192,0,38,55,0,32.75,20.25],[0,63,38,55,0,32.75,20.25],[38,63,0,0,0,-0.25,-0.75]]; var anim02=[[38,63,39,40,0,17.65,32.95],[77,63,44,40,0,20.65,32.95],[121,63,57,40,0,39.65,32.95],[178,63,54,39,0,48.65,31.950000000000003],[0,118,54,39,0,48.65,31.950000000000003],[54,118,56,35,0,53.65,26.950000000000003],[110,118,56,35,0,53.65,26.950000000000003],[166,118,0,0,0,-0.3500000000000014,-0.04999999999999716]]; var anim03=[[166,118,30,46,0,13.899999999999999,35.25],[196,118,35,47,0,18.9,36.25],[0,165,42,62,0,23.9,53.25],[42,165,47,49,0,28.9,56.25],[89,165,47,49,0,28.9,56.25],[136,165,46,39,0,28.9,59.25],[182,165,46,39,0,28.9,59.25],[228,165,0,0,0,-0.10000000000000142,-0.75]]; var n01=anim01.length; var n02=anim02.length+n01; var n03=anim03.length+n02; var data = { images: [HResources.getResourceByTag("cat")], frames:anim01.concat(anim02).concat(anim03), animations: { state01:[0,n01-1], state02:[n01,n02-1], state03:[n02,n03-1] } }; var spriteSheet = new createjs.SpriteSheet(data); var sp= new createjs.Sprite(spriteSheet);
anim01、anim02、anim03对应3个动画,这3个数组是从flash中导出的。flash中,选中影片剪辑,生成sprite表,数据格式选择easeljs,会生成一个图片和一个js文件,js文件中有这个数组。
var spriteSheet = new createjs.SpriteSheet(data);
var sp= new createjs.Sprite(spriteSheet);
都比较简单,关键在于data的定义。可以多看看官方API中SpriteSheet的部分。frames数组以及图片可以借助其他工具生成,比如flash。
猫的动画播放。
猫跳的时候,需要播放相应的动画,动画播放结束之后,需要把猫恢复到初始的状态,并且移动到新的位置上。
如何才能知道动画播放结束呢?
flash中,我们是每一帧判断currentFrame是否等于totalFrames。createJs中提供了一个animationend事件,动画播放到最后一帧时,会发送animationend事件。
游戏逻辑。
游戏逻辑比较简单。
游戏初始化——>循环(用户点击——>猫跳一步)——>游戏结束
寻路算法
如果会Astar算法的话,这个就比较简单了,如果不会的话,先去学学Astar算法,网上有很多教程。
本次游戏中用到的寻路算法如下。
//寻路 function getPath(){ var startP=cat.p; var openTable=[]; var tempTable=[]; openTable.push(startP); var stepTable=[]; for(i=0;i<WN;i++){ stepTable[i]=[]; for(j=0;j<HN;j++){ stepTable[i][j]=0; } } var exitP; var canMove=false; var n=0; out:while(1){ n++; while(1){ var p=openTable.pop(); if( !p){ break; } var neighbor=p.j%2==0?NEIGHBOR_01:NEIGHBOR_02; for(i=0;i<6;i++){ var ii=p.i+neighbor[i][0]; var jj=p.j+neighbor[i][1]; if(ii=WN||jj>=HN){ continue; } if(ii==startP.i&&jj==startP.j){ continue; } if(mapState[ii][jj]!=0){ continue; } if(stepTable[ii][jj]){ continue; } var p1={"i":ii,"j":jj,"parent":p}; tempTable.push(p1); stepTable[ii][jj]=n; canMove=true; if(ii==0||jj==0||ii==WN-1||jj==HN-1){ exitP=p1; break out; } } } if(tempTable.length==0){ break; } openTable=tempTable; tempTable=[]; } /* for(i=0;i<WN;i++){ console.log(stepTable[i]); } */ if(!canMove){ //没有路了 //console.log("没有路了"); return null } if(!exitP){ //没有出口 //console.log("没有出口"); return {}; } //console.log("出口位置:");console.log(exitP); var nextP=exitP; while(nextP.parent!=startP){ nextP=nextP.parent; } return nextP; }
这个比Astar算法简单。
思路如下:
猫的位置标0;
相邻的位置(6个),可以跳过去的标1,
和刚才标1的位置相邻的,并且没有检测过的位置标2;
……
直到到达边界(找到出口),或者没有相邻的了(没有出路了)。
从出口位置往前倒,找到猫下一步的位置。让猫跳过去。
如果没有到达出口位置,并且猫的周围还有可以跳过去的位置,那么可以随便选个位置让猫跳过去。
判断结束
判断结束比较简单,猫到了最边上,游戏失败。猫周围没有位置可以跳过去,游戏成功。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。