概述:最近看了点算法,为了对其有深刻的体会,利用周末时间撸了一个简易版的三消游戏,采用JS+Canvas实现,没有使用额外的游戏引擎,对于初学者来说,也比较容易入门的。下面是小游戏效果展示:
效果展示#
;
这还是一个比较初级的版本,大家有什么想法欢迎点评;
界面设计:
第一步开始游戏界面设计:
思路如下:
先在项目下新建一个js/runtime/background.js,来渲染不同阶段的背景(具体代码见github)
同时创建一个js/runtime/music.js来存放游戏各个阶段所需要的背景音乐(具体代码见github)
同时在背景图片上绘制开始游戏,并且进入游戏区域;
代码如下:
step1() {
ctx.clearRect(0, 0, canvas.width, canvas.height) //清楚上一局动画
this.bg.render(ctx);// 背景渲染
this.login.render(ctx);//开始游戏 渲染
this.touchHandler = this.touchEventHandler.bind(this) //开始游戏点击事件
canvas.addEventListener('touchstart', this.touchHandler)
}
touchEventHandler(e) {
e.preventDefault()
let x = e.touches[0].clientX
let y = e.touches[0].clientY
let area = this.login.btnArea //开始游戏可点区域
if ( x >= area.startX
&& x <= area.endX
&& y >= area.startY
&& y <= area.endY ) {
this.secenceChange() //进入游戏主题区域
}
}
效果如下:
点击开始游戏,进入消除页面,先绘制消除界面
secenceChange() {
canvas.removeEventListener('touchstart',this.touchHandler) //移除上一局事件
this.up = this.update.bind(this)
this.bgupdate()
this.aniId = window.requestAnimationFrame(this.up,canvas)
this.music.secen2()
}
update() {
this.enemyGenerate();//随机生成小动物
this.initEvent();//为画布初始化事件
this.gameinfo.renderImage(ctx,this.score.num)//记录分数和关卡
}
效果如下:
触摸小动物使其发生位移,核心代码如下:(具体见github地址)
touchHanderMove(e) {
e.preventDefault()
let x = e.touches[0].clientX-ceilX
let y = e.touches[0].clientY-ceilY
this.endCoordinates = [x, y];
//点击 交换区域
if(x>0&&y>0&&x<(ceilX+imgW*numberX)&&y
核心算法
消除算法:
我采用了动态规划的思想,在双重遍历的时候判断相同就给一个match标记,在消除的时候就判断match是否大于0,
大于0就表示该小动物可以消除,然后再用canvas清空方格;
先判断是否有重复元素:使用isReapet函数
游戏下落算法:
在我们将相应的方块白色之后,其上面的方块应该下落,在这里我的思想是:下落我采用的是从下到上,从左到右的思想,将重复的小动物跟上面未重复的小动物进行位置交换,按照列遍历二维数组,定义一个变量temp,指向为0 的小动物的位置,一旦遇到晓东为为 0 的格子就将其与temp所指的小动物进行交换,一次类推这样的话我们就可以把为空的上移到最顶层,并且不打乱顺序,然后我们在随机填充顶部的空方块就可以了。做完填充之后我们要再做一次消除算法;
down() {
for(let x =numberY-1;x>-1;x--) {
for(let y=0 ; y-1;k--) {
if(this.boxData[y][k].match==0) {
let temp = this.boxData[y][k];
this.boxData[y][k] = this.boxData[y][x]
this.boxData[y][x] = temp;
break;
}
}
}
}
this.downgame();
}
点击重玩,重新生成小动物
在交换和小动物下落的时候再加入音乐素材,后续就是绘制动画了;然后一个低配的消消乐就完成了;
然后再申请一个小游戏账号把代码上传后续其他人就可以进行体验了;
游戏性能优化
一款好的游戏,离不开性能优化,我从以下几点来为改游戏做了优化;
1:离屏 Canvas
在点击开始游戏,进入玩区后,需要反复的擦除和重绘,为了动画的流畅,我使用离屏 Canvas进行绘制的,像网格背景、背景图、关卡等。,并且使用了 小游戏封装的requestAnimationFrame函数来绘制动画,在小游戏 首次调用创建的是显示在屏幕上的画布,之后调用创建的都是离屏画布。初始化时将静态场景绘制完备,需要时直接拷贝离屏Canvas的图像即可。Canvas 绘制本身就是不断的更新帧从而达到动画的效果,通过使用离屏 Canvas,就大大减少了一些静态内容在上屏Canvas的绘制,从而提升了绘制性能使动画流畅。
2:内存优化
玩家在游戏过程中拖动小动物的移动其实就是不断更新坐标信息,然后不断的清空画布再重新绘制,可以想象,这个绘制是非常频繁的,按照普通的做法就需要不断去创建多个新的对象。针对游戏中需要频繁更新的对象,我使用对象池的方法进行优化,对象池维护一个装着空闲对象的池子,如果需要对象的时候,不是直接new,而是从对象池中取出,如果对象池中没有空闲对象,则新建一个空闲对象,就像官方demo实现的对象池类那样;
3:垃圾回收
小游戏中,JavaScript 中的每一个 Canvas 或 Image 对象都会有一个客户端层的实际纹理储存,实际纹理储存中存放着 Canvas、Image 的真实纹理,通常会占用相当一部分内存。
每个客户端实际纹理储存的回收时机依赖于 JavaScript 中的 Canvas、Image 对象回收。在 JavaScript 的 Canvas、Image 对象被回收之前,客户端对应的实际纹理储存不会被回收。通过调用 wx.triggerGC() 方法,可以加快触发 JavaScriptCore Garbage Collection(垃圾回收),从而触发 JavaScript 中没有引用的 Canvas、Image 回收,释放对应的实际纹理储存。
但 GC 具体触发时机还要取决于 JavaScriptCore 自身机制,并不能保证调用 wx.triggerGC() 能马上触发回收,本游戏在点击游戏开始或重玩都会触发一下,及时回收内存垃圾,以保证拥有良好的游戏体验。
兼容测试:
我让我身边的小伙伴们玩了一下,目前在ios或者安卓上基本上都是ok的
结语:
本文章讲述有点简单,详情见https://github.com/xfbaby/game.git,在游戏的优化上还有写需要改进,后续还会持续为大家分享,希望能对小游戏感兴趣的鞋童们有所帮助;