最近在玩js,感知js确是一个强大的脚本语言,加之html5这样一个准标准的推出,相信js在未来的web app中发挥的作用会越来越大。过去几年时间都在做后台方面的一些开发,很少会触及到web方面的开发,作为一名软件工程师,多补充一点新的知识,为自己充充电还是很有必要,且有趣的。
是一款风靡全球的电视游戏机和掌上游戏机游戏,它由俄罗斯人阿列克谢·帕基特诺夫发明,故得此名。俄罗斯方块的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。由于上手简单、老少皆宜,从而家喻户晓,风靡世界。
在网页中设计这样一款游戏:
1.在界面方面,我们采用html标签“table”作用这款游戏的整体布局,“table”的子标签“td”为最基本的方块单元。
2.通过改变方块单元的颜色来表示每个方块形状的移动过程。
3.接收并处理DOM模型中的document的onkeydown事件,从而左、右、下移方块形状。
首先是设计游戏地图的数据结构。
游戏主界面如下图所示:
地图的数据结构为grid和gridBuf两部分,初始时gridBuf是grid的一个备份,只有当接收到了新的box(方块形状)从而产生变化时,grid和gridBuf相互配合保持图形和逻辑的一致,下面是grid的内容:
//map: 12x17 var grid = [ [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,1], [1,1,1,1,1,1,1,1,1,1,1,1] ];接下来是所有的box(方块形状)的数据结构:
var boxdata = [ [ [1,1,1,1], [0,0,0,0], [0,0,0,0], [0,0,0,0] ], [ [1,1,1,0], [1,0,0,0], [0,0,0,0], [0,0,0,0] ], [ [1,1,1,0], [0,1,0,0], [0,0,0,0], [0,0,0,0] ], [ [1,1,0,0], [0,1,1,0], [0,0,0,0], [0,0,0,0] ], [[0,1,1,0], [1,1,0,0], [0,0,0,0], [0,0,0,0] ], [ [1,1,0,0], [1,1,0,0], [0,0,0,0], [0,0,0,0] ] ];对于js这种脚本语言,我们是通过以一个定义函数的形式来定义对象,下面就是Box对象的定义体:
function Box(x, y, arr, color) { this.arr = arr; this.x = x; this.y = y; this.w = getWidth(arr); this.h = getHeight(arr); this.color = color; this.active = true; this.clearOldBox = function() { for(var j = 0; j <= this.h; j++) for(var i = 0; i <= this.w; i++) if(this.arr[j][i] > 0) grid[this.y+j][this.x+i] = 0; } this.putNewBox = function() { for(var j = 0; j <= this.h; j++) for(var i = 0; i <= this.w; i++) if(this.arr[j][i] > 0) grid[this.y+j][this.x+i] = this.color; } this.moveLeft = function() { this.clearOldBox(); var _x = this.x - 1; if(this.canMove(_x, this.y)) this.x = _x; this.putNewBox(); drawGrid(); } this.moveRight = function() { this.clearOldBox(); var _x = this.x + 1; if(this.canMove(_x, this.y)) { this.x = _x; } this.x = _x; this.putNewBox(); drawGrid(); } this.moveDown = function() { this.clearOldBox(); var _y = this.y + 1; if(this.canMove(this.x, _y)) { this.y = _y; this.putNewBox(); drawGrid(); } else { this.putNewBox(); drawGrid(); checkLineFull(); return ; } } this.rotate = function() { var i,j; var tmp = [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]]; for(j = 0; j <= this.h; j++) for(i = 0; i <= this.w; i++) tmp[this.w-i][j] = this.arr[j][i]; var newBox = new Box(this.x, this.y, tmp, this.color); this.clearOldBox(); if(!newBox.canMove(this.x, this.y)) this.putNewBox(); else { box = newBox; box.putNewBox(); drawGrid(); } } this.canMove = function(x, y) { for(var j = 0; j <= this.h; j++) for(var i = 0; i <= this.w; i++) { if(grid[y+j][x+i] != 0 && this.arr[j][i] != 0) return false; } return true; } }基本的数据结构建立好之后,就是要讲这些内容显示在我们的网页上了,正如第二小节所说,我们使用td作为最基本方块单元的显示,用颜色来区分地图和方块形状:
function drawGrid() { for(var j = 0; j < 17 ; j++) for(var i = 0; i < 12; i++) { if(grid[j][i] != gridBuf[j][i]) paintCell(j, i, grid[j][i]); gridBuf[j][i] = grid[j][i]; } } // paint only one cell function paintCell(i, j, color) { var htmlGrid = document.getElementById("TetrisGrid").firstChild; htmlGrid.childNodes[i].childNodes[j].style.backgroundColor = colors[color]; } function init() { var html = '<table id="TetrisGrid" cellspacing=1 style="background-color:green"><tbody>'; for(var i = 0; i < 17; i++) { html += '<tr>'; for(var j = 0; j < 12; j++) { html += '<td width="20" height="20" style="background-color:' +colors[grid[i][j]] + ';"></td>'; } html += '</tr> \r\n'; } html += '</tbody></table>'; document.write(html); }接下来,是最该俄罗斯方块最重要的一部分——事件处理部分。也就是说从接受键盘事件,到通过定义事件handle来处理相应的事件,并通过改变数据内容从而反馈到界面上来:
function checkLineFull() { var full, i, j, i2; var y3 = box.y + box.h; var y4 = box.y; for(i = y3; i >= y4;) { full = 1; for(j = 1; j < 10; j++) if(grid[i][j] == 0) { full = 0; break; } if(full == 0) { --i; continue; } for(i2 = i; i2 > 0; i2--) for(j = 1; j < 10; j++) grid[i2][j] = grid[i2-1][j]; drawGrid(); y4++; gotLine++; } checkGameOver(); } function checkGameOver() { var bOver = false; for(var j = 1; j < 10; j++) if(grid[1][j] > 0) { bOver = true; break; } if(!bOver) { box = new Box(1,1,boxdata[Math.floor(Math.random()*7)], Math.floor(Math.random()*5)+1); box.putNewBox(); } else { isGameOver = true; msg.innerHtml = "Game Over! You score is " + gotLine*100; window.clearInterval(); } } function document_onkeydown() { if(isGameOver) return; switch(event.keyCode) { case 37: box.moveLeft(); break; case 39: box.moveRight(); break; case 38: box.rotate(); break; case 40: box.moveDown(); break; default: break; } } function moveDownBox() { var interval = 1000 - 10 * (gotLine>80 ? 80 : gotLine); msg.innerHTML = "Level: " + Math.floor(gotLine/10) + " score:" + gotLine*100; box.moveDown(); window.setTimeout('moveDownBox()', interval); }我们是这样来处理事件逻辑的:
1.box最基本的事件是MoveDownBox。
2.当box完全掉下时,判断是否可以抵消,通过checkLineFull函数判断。
3.检查游戏是否结束(checkGameOver):
3.1 是,提示用户游戏结束,over了。
3.2 否,new一个新的box重复以上过程。
游戏的启动和事件设置如下:
function startGame() { init(); //setTimeout will delay for 1000ms and then execute moveDownBox(). window.setTimeout('moveDownBox()', 1000); isGameOver = false; box = new Box(1,1,boxdata[Math.floor(Math.random()*7)], Math.floor(Math.random()*5)+1); box.putNewBox(); drawGrid(); }
<script language="javascript" for="document" event="onkeydown"> if(document.all) document_onkeydown(); </script>
使用javascript开发游戏是一个很有趣的过程,它的语法接近C语言,对于习惯于C语言开发的同学们,是比较容易上手的,它本身的语法也很简单,而且它也是一门面向对象的语言,本身拥有面向对象的特性;如果要深入对javascript的学习的话,可以结合jQuery,html5进一步学习。
《javascript入门教程》
《javascript从入门到精通》