HTML5中新增了Canvas元素,这个元素非常好玩,使用Canvas,我们可以使用代码绘制出我们想要的图形,用代码绘图,光是想想就兴奋啊。
于是我在学习了Canvas的部分内容后,动手制作了一款小游戏,这也是本人第一次独立开发web项目,所以内行人看到细节肯定忍不住吐槽,希望大家批评指正,给出宝贵意见,我们共同进步。
一、游戏介绍:
这个游戏的界面非常简单,左边一块用Canvas绘制的画布,右边有4个按钮,左边的画布上有一个红色的方块和一个黑色的方块,红色的方块是我们操作的对象,它是一个1×1×2的长方体,现在它正立在画布上,将1×1的面朝向玩家,我们可以点击右侧的四个按钮操作这个长方体,让这个长方体在画布上滚动,根据用户的点击,这个长方体会将不同的面朝向玩家,而玩家的目标就是通过操作这个长方体将它插入到画布上黑色方块所代表的洞中,另外,如果玩家的操作会导致长方体的任何部位悬空,那操作是无法执行的,会提示“无法前进”!目前这个游戏的功能比较简单,难度也不大,如果经过后期修改完善,增加更多无法前进的区域,这个游戏是很有挑战性的。
按下向下按钮,长方体向下滚动,原来的图案变成了这样:
如果此时按左键,会弹出错误警告:
按下右键,也会导致长方体的部分悬空,所以也会弹出同样的警告
最终我们要将长方体完整无误地插入洞中,也就是大概做到这样:
这时,只要按下向上,就能将长方体插入洞中,完成目标:
下面来谈谈具体实现。
二、设计思路及代码实现
游戏的实现主要是利用了Csnvas元素的绘图方法,通过用户点击按钮,产生指令,将指令传递给特定的函数,通过函数判断长方体是否可以前进,将会产生怎样的结果,然后通过Canvas重绘图形。
1、首先,我们将游戏界面绘制出来,
<canvas id="map" style="border:3px solid;float:left" width="500px" height="500px">你的浏览器不支持canvas</canvas> <!--按钮--> <br /> <div id="control"> <div class="up" onClick="oTangle.moveUp()"><img src="arrow_up_128px_1137748_easyicon.net.png" /></div> <br /> <div class="left" onClick="oTangle.moveLeft()"><img src="arrow_left_128px_1137746_easyicon.net.png" /></div> <div class="down" onClick="oTangle.moveDown()"><img src="arrow_down_128px_1137745_easyicon.net.png" /></div> <div class="right" onClick="oTangle.moveRight()"><img src="arrow_right_128px_1137747_easyicon.net.png" /></div> </div>
以上代码制作了界面的主体布局,包括一张画布,和包含4个作为按钮的div的控制区域。
然后为以上元素添加样式:
<style> div#control { float:left; width:500px; height:500px; position:absolute; left:500px; top:0; } div#control div { width:100px; height:100px; border-radius:25px; text-align:center; padding : 0px; box-shadow:2px 2px 1px #CCC; } div.up { position:absolute; left:200px; top:80px; } div.left { position:absolute; left:80px; top:200px; } div.right { position:absolute; left:320px; top:200px; } div.down { position:absolute; left:200px; top:320px; } img { opacity:0.2; filter:alpha(opacity=40); } img:hover { opacity:1.0; filter:alpha(opacity=100); } </style>
然后,使用以下代码绘制地图和黑洞:
var oLines = document.getElementById("map").getContext("2d"); for(var i =0;i<4; i++) { oLines.moveTo(100+100*i,0); oLines.lineTo(100+100*i,500); oLines.stroke(); oLines.moveTo(0,100+100*i); oLines.lineTo(500,100+100*i); oLines.stroke(); } oLines.save(); oLines.fillStyle="#000000"; oLines.fillRect(100,100,100,100); oLines.restore();
将以上代码封装到一个map()函数中,用于以后调用
然后,绘制我们操作的长方体,初始状态下,长方体是立着的,代码如下:
var tangle = document.getElementById("map").getContext("2d"); tangle.fillStyle = "#FF00FF"; tangle.fillRect(0,0,100,100);
至此,我们绘制出了游戏所需要的游戏界面,下面我们通过代码实现游戏的操作
2、定义对象
我们可以定义一个对象来代表这个长方体,这个对象应该包含以下属性:
状态:表示长方体当前是直立、横躺还是竖躺的字符串变量。
坐标:表示长方体在画布上的位置(这里用长方形左上角的点)的Number类型变量。
长、宽、高:用于重绘时进行计算的数字常量。
对象还应包含表示向各个方向滚动的4个方法,与四个按钮的响应事件向对应
具体如下:
var oTangle = new Object; oTangle.sErect = "erect"; /*长方体的状态*/ oTangle.x = 2; /*当前坐标x*/ oTangle.y = 2; /*当前坐标y*/ /*代表的显示在地图上的长方形的左上角的坐标*/ oTangle.length = 100; oTangle.width = 100; oTangle.height = 200; oTangle.moveRight = function()/*长方体的4个方法*/ { move(oTangle.x,oTangle.y,oTangle.sErect,"right"); }; oTangle.moveLeft = function() { move(oTangle.x,oTangle.y,oTangle.sErect,"left"); }; oTangle.moveUp = function() { move(oTangle.x,oTangle.y,oTangle.sErect,"up"); }; oTangle.moveDown = function() { move(oTangle.x,oTangle.y,oTangle.sErect,"down"); };
然后,我们要抽象化这张画布,方便长方体对象进行路径判定,这里可以使用一个数组表示:
var mapArray = new Array(); mapArray[0] = new Array(0,0,0,0,0,0,0,0,0); mapArray[1] = new Array(0,0,0,0,0,0,0,0,0); mapArray[2] = new Array(0,0,1,1,1,1,1,0,0); mapArray[3] = new Array(0,0,1,1,1,1,1,0,0); mapArray[4] = new Array(0,0,1,1,1,1,1,0,0); mapArray[5] = new Array(0,0,1,1,1,1,1,0,0); mapArray[6] = new Array(0,0,1,1,1,1,1,0,0); mapArray[7] = new Array(0,0,0,0,0,0,0,0,0); mapArray[8] = new Array(0,0,0,0,0,0,0,0,0);
在这里,除了画布本身的5×5个坐标,我还把外围2层也加入到数组中去了,这样是为了应对向外翻滚两格的情况,当然,也可以使用instanceof运算符,判断坐标是否为number类型,这样就只需要建立一个5×5的二维数组就可以了,如果翻滚导致坐标变成数组的length以外,将会返回Undefined,以此进行判定。
3、对象的方法——move()函数
我们创建一个函数,接受oTangle对象的属性作为参数,然后将参数传递到其他子程序中
function move(posiX,posiY,state,direction) { if(state=="erect") { if(ifSuspend(posiX,posiY,"erect",direction)) { draw(posiX,posiY,"erect",direction); map(); posiX=oTangle.x; posiY=oTangle.y; } else { alert("无法前进!"); } } 其他情况的判定...
在这个move()函数中,函数接受从对象的方法传递来的参数,调用一个判定前方是否为路径的函数,然后判断函数的返回值是否为true,即路径合法,如果路径合法,执行draw()函数和map()函数重绘图形,并让posiX、posiY与被修改的oTangle对象的坐标属性同步,如果路径不合法,则弹窗报错。根据长方体状态的不同,共有3种判定情况,这里只给出竖直情况的方法。
4、路径判定函数——ifSuspend()函数
此函数接受从move()函数传递过来的oTangle对象属性值,根据对象状态的不同使用其坐标(不是当前坐标,而是玩家点击操作后的坐标值)和表示地图的二维数组进行判定:
function ifSuspend(posiX2,posiY2,state2,direction2){ var arrX=posiX2,arrY=posiY2; if(state2=="erect") { if(direction2=="left") { if(mapArray[arrX-2][arrY]) { if((posiX2==4 && posiY2==3)||(posiX2==5 && posiY2==3)) { return 0; } else { return 1; } } else { return 0; } } if(direction2=="right") { if(mapArray[arrX+2][arrY]) { if((posiX2==1 && posiY2==3)||(posiX2==2 && posiY2==3)) { return 0; } else { return 1; } } else { return 0; } } if(direction2=="up") { if(mapArray[arrX][arrY-2]) { if((posiX2==3 && posiY2==4)||(posiX2==3 && posiY2==5)) { return 0; } else { return 1; } } else { return 0; } } if(direction2=="down") { if(mapArray[arrX][arrY+2]) { if((posiX2==3 && posiY2==2)||(posiX2==3 && posiY2==1)) { return 0; } else { return 1; } } else { return 0; } } } 其他判断情况。。。
在函数的开始,新建了2个变量,将传递来的参数的值赋值给它们,因为参与判定的是oTangle对象操作后的坐标(此时还未判定是否允许操作),而不是当前的坐标。
上面给出了竖直情况下的判定,竖直状态下,向左和向上滚动会导致长方形左上角的坐标发生2个单位的偏移,而向右和向下滚动则会产生1个单位的偏移。如图:
红色的点是当前坐标,其他四个点的是将因玩家操作导致变化的结果,黑点相对红点左移2个单位(arrX-2),黄点相对红点右移2个单位(arrX+2),绿点相对红点上移2个单位(arrY-2),紫点相对红点下移1个单位(arrY-1).
在横躺和竖躺的情况下,还要考虑插入黑洞的情况,如果操作将导致长方形的坐标等于黑洞的坐标,将退出判定,弹窗告诉玩家游戏结束。
5、重绘图形——draw()函数
draw()函数使用JavaScript代码绘制图形,这里要调用前面绘制的tangle图形对象。
首先调用save()方法保存当前状态,主要是避免下次绘图时改变原点的位置
然后调用clearRect()方法清除现在的图形,接受4个参数,表示要清除的图形的区域坐标,(会一并清除地图上的网格线,所以在上面的move()函数中再次调用了map(),事实上,在下面的代码中,在clear()方法后面紧接着调用map(),就不会出现游戏中网格线在长方体上的BUG了,不过,不要在意这些细节是不是?)
重绘的实现:首先,清除的区域是
((posiX4-2)*oTangle.width,(posiY4-2)*oTangle.width,oTangle.width,oTangle.length)
这个区域表示长方体当前的图形,posiX4-2是因为初始的坐标是(2,2)(如果是用instanceof来判定,就不用这样了,罪孽啊)
fillRect()方法重绘矩形,同样接受4个参数,表示描绘矩形的区域,不同的状态,不同的方法,作为参数的公式也不相同,这里只给出竖直状态下左滚情况下的公式。
然后,回到前一个save()方法的状态,避免绘图导致原点偏移。
最后,修改长方体对象当前的坐标,修改长方体对象当前的状态,滚动完成。
function draw(posiX3,posiY3,state3,direction3) { var posiX4=oTangle.x,posiY4=oTangle.y; if(state3=="erect") { if(direction3=="left") { tangle.save(); tangle.clearRect((posiX4-2)*oTangle.width,(posiY4-2)*oTangle.width,oTangle.width,oTangle.length); tangle.fillRect((posiX4-4)*oTangle.width,(posiY4-2)*oTangle.width,oTangle.height,oTangle.width); tangle.restore(); oTangle.x-=2; oTangle.sErect="cross"; } 其他判断情况。。。
使用以上代码,就实现了游戏流程,这个游戏也就完成了
三、学习心得
这个游戏功能(我实现的部分)很简单,流程也不复杂,主要是使用了Canvas的绘制矩形的功能,很好理解,可以改进的地方也非常多,包括BUG也不少,不过毕竟这只是一个小小的作业,还有很多东西要学,所以后续的开发智能搁浅了。。。。