有一年夏天在家无所事事,就在w3school上自学了前端的一些基本内容。那段时间微信跳一跳很火,师兄说我可以试试在网页上写一个跳一跳。说干就干。对于这种小游戏,比较好的设计思路是:前台动画与后台逻辑分离以及面向对象的设计。这对于当时的我来说还是比较抽象的,好在编写这么一个小程序的过程中稍微理解了其中的含义。整个游戏相比微信跳一跳有以下不同:
由于并没有接触过webgl,以我的水平无法做出和微信跳一跳一样的炫酷3D效果,所以就只能用2D的方法做一个乞丐版的跳一跳了;由于不是触摸屏,所以用按压空格键代替长按屏幕实现蓄力;有一些细节上的效果比较不太好实现,例如:小人没有跳到方块上的跌倒动画;没有任何音频效果;效果图,gif比较粗糙,实际的动画效果会更加流畅:
仔细分析一下跳一跳的游戏,其实可以将游戏拆分成,可变化颜色的背景、有两个方向随机掉落的方块、以及一个基本上大部分时间都呆在视角中心点的小人。因此面向对象相对比较好理解,可以拆分成如下的几个对象: 1.小人一定是一个独立的个体,它有很多只属于自己的属性,例如当前自己的坐标,当前自己所处的状态(跳跃中,落在方块上等) 2.每一个方块也是如此 3.游戏中一个非常重要的对象就是camera对象。在这个游戏中,视角的移动是很重要的。 4.得分管理
动画与后台逻辑分离,现在在我看来是,游戏一旦开始运行就应当进入一个循环,以一个确定的周期去更新整个游戏,例如上面提到的所有的状态参数,而前台玩家看到的画面仅仅是在每一次刷新时提取后台逻辑提供的参数更新自身而已。
曾经看到过一本讲如何用HTML5制作一个游戏的书,其中讲到的一个机制比较类似于C#中的composite_targetrendering事件,将屏幕刷新事件作为整个系统的时基,又有点像使用ucos时的滴答定时器事件。可惜后来没试成功,所以就直接使用一个定时器来驱动整个游戏的刷新了。
function start() { if(isOver) { isOver=false; game=setInterval(Jump,roundTime); //设置周期执行Jump函数 } } function Jump() { gameLoop(); //逻辑 surfaceLoop(); //可视化 }后台逻辑
function gameLoop() //逻辑循环 { Init(); switch(state) { case 0: getNewCube(); break; case 1: player1.accumulation(currIndex); break; case 2: //恢复方块缓动后的参数 player1.isAccumulated=false; cubes[currIndex].coordinateY=cubes[currIndex].restorecoordinateY; //恢复currIndex方块的纵坐标 cubes[currIndex].cubeBoardHeight=40; //恢复currIndex方块的侧板高度 cubes[currIndex].QT=0.2; //恢复currIndex方块的Q弹系数 player1.judge( ); break; case 3: gameOver(); break; case 4://跳跃动画——完成后再进入2判断 player1.parabola(); player1.bright(currIndex); break; } }前台界面
function surfaceLoop() { drawBackground(); fraction1.drawScore(); switch(state) { case 0: //空闲 camera1.cameraAdjusting(); //空闲状态时的视点平移 drawCube(); player1.drawPlayer(player1.coordinateX,player1.coordinateY); break; case 1://蓄力 camera1.cameraAdjusting(); //确保在蓄力时仍能够正常视点平移 player1.drawPlayer(player1.coordinateX,player1.coordinateY); drawCube(); //蓄力时偷换所有方块 //document.getElementById("des").innerHTML="蓄力中……"; break; case 2://判断 canvasClear("boardLayer"); //落地后清除“脏块” drawCube(); //绘制新状态下的方块 break; case 3://失败 break; case 4://跳跃 camera1.cameraAdjusting(); //确保在视点调节时,若发生由按键松开导致进入跳跃环节的情况仍能继续调节视点 drawCube(); //绘制方块Q弹效果 player1.drawPlayer(player1.preCoordinateX,player1.preCoordinateY); break; } }其实有了上述基本框架整个游戏的编写就已经成功了一半了,后面主要是关于以下几个要点的考虑: 1.坐标系的确定,这个确定了才可以去确定小人、方块、camera的坐标; 2.小人跳跃后的坐标计算问题; 3.完成一次弹跳从开始前到结束后的整体逻辑流程;
1.小人落在方块上时会需要判断距离方块中心的距离,以此来计算奖励加成; 2.方块初次出现的时候是一个掉落的过程,并且落地时会有一个Q弹的效果,此外在小人蓄力的时候,可以很明显的看到方块也有一个压缩的过程并且在小人离开的时候也会有一个Q弹的效果; 3.小人最开始落在方块上时同样有一个Q弹的效果; 这种落地的Q弹效果我使用如下的一个函数:
function falling() { this.factor=1-(-1)/2*Math.pow(Math.E,((-1)*6*this.fallingT))*((-1)*2*Math.pow(Math.E,6*this.fallingT)+Math.sin(12*this.fallingT)+2*Math.cos(12*this.fallingT)); if(this.factor<=0) { this.factor*=(-1); } this.altitude=200*this.factor; this.fallingT+=0.05; }4.在图片上作弊,尽可能实现3D效果,小人增加了一些阴影效果,方块则是为了实现压缩而在蓄力的时候偷换成如下的组成:
其实最后整个程序写下来也没有很多内容,只要一个.htm文件加一堆图片素材,还有就是一个chrome就可以运行了。
总的来说算是我第一次尝试做一个小项目,的确最开始的机制设计是最耗时间的,机制和架构设计好后接下来就是细节的完善和补充。