读书人

js版俄罗斯方块(2)

发布时间: 2013-03-28 10:20:24 作者: rapoo

js版俄罗斯方块(二)

之前曾发过一个js版的俄罗斯方块,界面比较简单,Bug也不少。抽空重构了一下,加入了javaScript面向对象的知识,修复了一些明显的BUG。

斌斌 (给我写信) 原创博文(http://blog.csdn.net/binbinxyz),转载请注明出处!

源码(下载:http://download.csdn.net/detail/binbinxyz/5186774)公开如下:

<!DOCTYPE html><html>  <head>    <title>俄罗斯方块_经典游戏</title>    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="keywords" content="游戏, 经典游戏, 俄罗斯方块, tetris"><meta http-equiv="description" content="斌斌的小游戏:俄罗斯方块"><link rel="icon" href="http://www.ifeng.com/favicon.ico" type="image/x-icon" /><link rel="shortcut icon" href="http://www.ifeng.com/favicon.ico" type="image/x-icon" /><link rel="bookmark" href="http://www.ifeng.com/favicon.ico" type="image/x-icon" /><!-- 游戏名称:俄罗斯方块(Tetris)<br/>游戏版本:1.1<br/>作者:斌斌<br/>邮箱:cn.binbin@qq.com<br/>网站:http://blog.csdn.net/binbinxyz<br/>--><style type="text/css"><!--  .thinBorderTable{border-collapse:collapse;}.thinBorderTable th{background:#ffffff;border:solid 1px #000000;}.thinBorderTable td{background:#ffffff;border:solid 1px #000000;}.outerBorderTable{border-collapse:collapse;}.outerBorderTable th{background:#ffffff;}.outerBorderTable td{background:#ffffff;}--></style>  </head>  <body>  <div id="game" align="center">  <h2>俄罗斯方块  <span style="font-size: 14px">V1.1</span></h2>  <table>  <tr>  <td>  <fieldset><legend>俄罗斯方块的<b>基本规则</b></legend>  <table id="info"><tr valign="top"><td>1、</td><td width='400px'>一个用于摆放小型正方形的平面虚拟矩形,标准大小:宽为10,高为20,以小正方形为单位。</td></tr><tr valign="top"><td>2、</td><td width='400px'>一组由4个小型正方形组成的规则图形即方块(Tetromino ),共有7种,分别以S、Z、L、J、I、O、T这7个字母的形状来命名。</td></tr><tr valign="top"><td>3、</td><td width='400px'>部分游戏有单格方块,可以穿透固定的方块到达最下层空位。</td></tr><tr valign="top"><td>4、</td><td width='400px'>玩家的操作有:以90度为单位旋转方块,以格子为单位左右移动方块,让方块加速落下。</td></tr><tr valign="top"><td>5、</td><td width='400px'>方块移到区域最下方或是落到其他方块上会固定在该处,而新的方块从区域上方开始落下。</td></tr><tr valign="top"><td>6、</td><td width='400px'>当区域中某一行横向格子全部由方块填满,则该行会消失。</td></tr><tr valign="top"><td>7、</td><td width='400px'>当固定的方块堆到区域最上方而无法消除层数时,则游戏结束。</td></tr><tr valign="top"><td>8、</td><td width='400px'>游戏会提示下一个要落下的方块。</td></tr><tr valign="top"><td>9、</td><td width='400px'>计分标准:下落一个方块1分,一次消一行10分、2行30分、3行60分、4行100分。</td></tr></table></fieldset><fieldset><legend>游戏按键提示</legend><table style="width: 430px;margin: 0px;padding: 0px"><tr><td><b>↑</b>:旋转方块</td><td><b>↓</b>:方块下移</td><td><b>←</b>:方块左移</td><td><b>→</b>:方块右移</td></tr><tr><td colspan="4">空格键(<b>Spacebar</b>):方块降落(直接下移到位)</td></tr></table></fieldset>  </td>  <td>  <fieldset><legend>显示屏幕</legend><div id='screen' style="background-color: #aaaaaa;padding: 5px"></div></fieldset>  </td>  <td>  <div style="width: 200px;">  <fieldset>  <legend>显示区域</legend>  <table>  <tr><td>当前级别:</td><td><span id='level'>1</span></td></tr><tr><td>当前分数:</td><td><span id='score'>0</span></td></tr><tr><td>运行时间:</td><td><span id='runTime'>00:00</span></td></tr><tr><td colspan='2'><div id="smallScreen"></div></td></tr></table></fieldset><fieldset id="configArea" style="display: none;"><!-- style="display: none;" --><legend>配置区域</legend><table><tr><td>显示屏幕辅助线:</td></tr><tr><td><label><input type='radio' id='lineVisible_1' name='lineVisible' value='Y' checked='checked'/>YES</label><label><input type='radio' id='lineVisible_2' name='lineVisible' value='N'/>NO</label></td></tr><tr><td>生成的方块颜色随机:</td></tr><tr><td><label><input type='radio' name='randomTetrominoColor' checked='checked' onclick='setRTColor(true)'/>YES</label><label><input type='radio' name='randomTetrominoColor' onclick='setRTColor(false)'/>NO</label></td></tr><tr><td>底部方块的颜色随机:</td></tr><tr><td><label><input type='radio' name='randomBottomColor' checked='checked' onclick='setRBColor(true)'/>YES</label><label><input type='radio' name='randomBottomColor' onclick='setRBColor(false)'/>NO</label></td></tr></table></fieldset><fieldset><legend>控制区域</legend><input id='btnstart' type='button' value='开始' onclick="start()"/><input id='btnpause' type='button' value='暂停' disabled="disabled" onclick='pause()'/><input id='btnend' type='button' value='结束' disabled="disabled" onclick='end()'/></fieldset>  </div>  </td>  </tr>  </table>  </div><script type="text/javascript">var COLUMN = 10; //显示屏幕的宽度(相对列数)var ROW = 20; //显示屏幕的高度(相对行数)//定义方块数据数组,存储所有的方块数据var TETROMINO_DATA = [      [ [[0,0,0,0],[1,1,1,1],  [0,0,0,0], [0,0,0,0]], [[0,1,0,0], [0,1,0,0], [0,1,0,0], [0,1,0,0]] ], [ [[1,1,1,0], [1,0,0,0], [0,0,0,0], [0,0,0,0]], [[1,1,0,0], [0,1,0,0], [0,1,0,0], [0,0,0,0]], [[0,0,1,0], [1,1,1,0], [0,0,0,0], [0,0,0,0]], [[0,1,0,0], [0,1,0,0], [0,1,1,0], [0,0,0,0]] ], [ [[0,0,0,0], [1,1,1,0], [0,1,0,0], [0,0,0,0]], [[0,1,0,0], [1,1,0,0], [0,1,0,0], [0,0,0,0]], [[0,1,0,0], [1,1,1,0], [0,0,0,0], [0,0,0,0]], [[0,1,0,0], [0,1,1,0], [0,1,0,0], [0,0,0,0]] ], [ [[0,0,0,0], [1,1,1,0], [0,0,1,0], [0,0,0,0]], [[0,1,0,0], [0,1,0,0], [1,1,0,0], [0,0,0,0]], [[1,0,0,0], [1,1,1,0], [0,0,0,0], [0,0,0,0]], [[0,1,1,0], [0,1,0,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,1,0], [0,1,1,0], [0,1,0,0], [0,0,0,0]] ], [ [[0,1,0,0], [0,1,1,0], [0,0,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], [0,1,1,0], [0,1,1,0], [0,0,0,0]] ] ];var POSSIBLE_COLORS = ["aqua", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "purple", "red", "silver", "teal", "yellow"]; //可能使用的颜色库var DEFAULT_TETROMINO_COLOR = "GRAY"; //生成方块的默认颜色var DEFAULT_BOTTOM_COLOR = "GRAY"; //底部方块的默认颜色var DEFAULT_BLOCK_X = 4; //方块出现时左上角默认位置的X坐标var bottom = []; //底部方块堆(实际是与屏幕大小相同的二维数组)var bottomColor = []; //底部方块堆中各块的颜色var level; //当前级别var period; //各后续任务之间的时间间隔,单位是毫秒var score; //当前分数var runTime; //运行时间,单位秒var block; //显示屏幕中的方块var block_x; //方块出现时左上角的X坐标var block_y; //方块出现时左上角的y坐标var gameStatus; //游戏状态(offDuty|run|pause|end)var lineVisible; //屏幕辅助线是否可见var randomTetrominoColor; //生成的方块颜色随机var randomBottomColor; //底部方块的颜色随机var taskTimer = function(){}; //计时器,用于循环执行方块下落等事件var updateTimer = function(){}; //计时器,游戏运行时用于更新游戏运行时间//定义方块对象的构造方法function Tetromino(color, index, status){this.color = color; //方块的颜色this.index = index;this.status = status;this.data = TETROMINO_DATA[index][status];};//获取当前方块的高度Tetromino.prototype.getHeight = function(){for(var i=3;i>=0;i--){for(j=0;j<4;j++){if(this.data[i][j] == 1){return i + 1;}}}};//获取方块对象的下一个状态Tetromino.prototype.getNextStatus = function(){this.status ++;this.status %= TETROMINO_DATA[this.index].length;return TETROMINO_DATA[this.index][this.status];};//alert(new Tetromino("blue", 2, 0).data);//定义屏幕显示的方块function Block(x, y, tetromino){this.x = x; //方块所在4×4矩形的左上角x坐标this.y = y; //方块所在4×4矩形的左上角y坐标this.tetromino = tetromino; //方块};//方块工厂,用于生成各种方块var TetrominoFactory = {tetromino: [], //方块属性createTetromino: function(){ //生成一个新的方块var color = DEFAULT_TETROMINO_COLOR; //使用默认颜色初始化//如果生成的方块颜色随机if(randomTetrominoColor){var i = Math.floor(Math.random() * POSSIBLE_COLORS.length);color = POSSIBLE_COLORS[i];//alert("0:" + color);}var index = Math.floor(Math.random() * TETROMINO_DATA.length);var status = Math.floor(Math.random() * TETROMINO_DATA[index].length);//alert(color + ":" + index + ":" + status);this.tetromino = new Tetromino(color, index, status);return this.getCurrentTetromino();},getCurrentTetromino: function(){ //返回当前的方块return this.tetromino;}};//alert("a:" + TetrominoFactory.createTetromino().data);//alert("b:" + TetrominoFactory.getCurrentTetromino().data);//初始化整个页面function initBody(){//初始化控制区域的按钮//document.getElementById("btnstart").removeAttribute("disabled");//document.getElementById("btnpause").disabled = "disabled";//document.getElementById("btnend").disabled = "disabled";initButtonStatus();//初始化全局变量level = 1;period = 800;score = 0;runTime = 0;block_x = DEFAULT_BLOCK_X;block_y = -3;gameStatus = "offDuty";lineVisible = true;randomTetrominoColor = true;randomBottomColor = true;initScreen();initSmallScreen();//初始化底部方块堆及堆中各块的颜色for(var i=0;i<ROW;i++){bottom[i] = [];bottomColor[i] = [];for(var j=0;j<COLUMN;j++){bottom[i][j] = 0;bottomColor[i][j] = "white";}}drawTetromino2smallScreen(TetrominoFactory.createTetromino());//next();}//初始化显示屏幕function initScreen(){var classStyle = "outerBorderTable";if(lineVisible){classStyle = "thinBorderTable";}//拼接显示屏幕的HTML代码var screenHtml = "<table id='screenTable' class='" + classStyle + "'>";for(var i=0;i<ROW;i++){screenHtml += "<tr height='20'>";for(var j=0;j<COLUMN;j++){screenHtml += "<td width='20'></td>";}screenHtml += "</tr>";}screenHtml += "</table>";document.getElementById("screen").innerHTML = screenHtml;}//初始化小显示屏幕function initSmallScreen(){//拼接小显示屏幕(用于显示下一个落下的方块)的HTML代码var smallScreenHtml = "<table class='thinBorderTable'>";for(var i=0;i<4;i++){smallScreenHtml += "<tr height='15'>";for(var j=0;j<4;j++){smallScreenHtml += "<td width='15'></td>";}smallScreenHtml += "</tr>";}smallScreenHtml += "</table>";document.getElementById("smallScreen").innerHTML = smallScreenHtml;}//在小显示屏幕绘制方块(tetromino)function drawTetromino2smallScreen(tetromino){//获取小显示屏幕var smallScreen = document.getElementById("smallScreen");//获取小显示屏幕中的所有行(即tr标记)var trs = smallScreen.getElementsByTagName("tr");for(var i=0;i<trs.length;i++){//获取当前行中的所有单元格var tds = trs[i].getElementsByTagName("td");for(var j=0;j<tds.length;j++){if(tetromino.data[i][j] == 1){tds[j].style.backgroundColor = tetromino.color;}else{tds[j].style.backgroundColor = "white";}}}}//将小显示屏幕上的方块移入显示屏幕中,并生成下一个方块显示在小显示屏幕上function moveTetromino2screen(){var tetromino = TetrominoFactory.getCurrentTetromino();block_y = -1 * tetromino.getHeight();block = new Block(block_x, block_y, tetromino);drawTetromino2smallScreen(TetrominoFactory.createTetromino());score += 1; //document.getElementById("score").innerHTML = score;}//显示屏幕刷新function reshow(){var style = "outerBorderTable";if(document.getElementById("lineVisible_1").checked){style = "thinBorderTable";}document.getElementById("screenTable").className=style;var screenDiv = document.getElementById("screen");//获取显示屏幕中的所有行(即tr标记)var trs = screenDiv.getElementsByTagName("tr");//alert("下落方块的坐标(x,y):" + block.x + "," + block.y);for(var i=0;i<trs.length;i++){//获取当前行中的所有单元格var tds = trs[i].getElementsByTagName("td");for(var j=0;j<tds.length;j++){//如果是底部的方块if(bottom[i][j] == 1){//alert("2:" + block.tetromino.color);tds[j].style.backgroundColor = bottomColor[i][j];}else{tds[j].style.backgroundColor = "white";}//下落的方块(当前方块的坐标位于4*4的矩形内部,且矩形内部对应位置的值是1)if(i>=block.y && i<block.y+4 && j>=block.x && j<block.x+4&& block.tetromino.data[i-block.y][j-block.x] == 1){//alert("3:" + block.tetromino.color);tds[j].style.backgroundColor = block.tetromino.color;}}}}//旋转function up(){//缓存旧数据var oldData = block.tetromino.data;block.tetromino.data = block.tetromino.getNextStatus();if(!isMoveable()){block.tetromino.data = oldData;}reshow();}//下移function down(){block.y ++; //向下移动一格if(!isMoveable()){block.y --;accept();}reshow();}//快速下移function quickDown(){while(true){block.y ++; //向下移动一格if(!isMoveable()){block.y --;accept();break;}reshow();}}//左移function left(){block.x --;if(!isMoveable()){block.x ++;}reshow();}//右移function right(){block.x ++;if(!isMoveable()){block.x --;}reshow();}//是否可以移动(即此次移动操作是否可行)function isMoveable(){var tetromino = block.tetromino;for(var i=0;i<tetromino.data.length;i++){for(var j=0;j<tetromino.data[i].length;j++){//左侧出界if(block.x+j<0 && tetromino.data[i][j] == 1){return false;}//右侧出界if(block.x+j>=COLUMN && tetromino.data[i][j] == 1){return false;}//到达底部了if(block.y+tetromino.getHeight()>ROW){return false;}//方块重合if(block.y+i>=0 && tetromino.data[i][j]==1 && bottom[block.y + i][block.x + j] == 1){return false;}}}return true;}//接收一个blockfunction accept(){var tetromino = block.tetromino;for(var i=0;i<tetromino.data.length;i++){for(var j=0;j<tetromino.data[i].length;j++){if(tetromino.data[i][j] == 1){if(block.y + i < 0){gameOver();return;}bottom[block.y + i][block.x + j] = 1;if(randomBottomColor){bottomColor[block.y + i][block.x + j] = tetromino.color;}else{bottomColor[block.y + i][block.x + j] = DEFAULT_BOTTOM_COLOR;}}}}var line = disappear();score += getScore(line); //changeLevel();document.getElementById("score").innerHTML = score;moveTetromino2screen();}//消去满行function disappear(){var line = 0; //此次消去行数var r = block.y + block.tetromino.getHeight() - 1;//alert(bottom[i].length);for(var i=0;i<block.tetromino.getHeight();i++,r--){//当前行是否可以消去,默认可消去var disapperable = true;for(var j=0;j<bottom[r].length;j++){//alert(r + ":" + j + ":" + bottom[r][j]);//如果有单元格为空,则当前行不可消去if(bottom[r][j] == 0){disapperable = false;break;}}if(disapperable){line ++; //更新消去行数//消去当前满行for(var k=r;k>0;k--){for(var j=0;j<bottom[k].length;j++){bottom[k][j] = bottom[k-1][j];if(bottomColor[k-1][j] != "white"){bottomColor[k][j] = bottomColor[k-1][j];}}}//首行全部为零for(var j=0;j<bottom[i].length;j++){bottom[0][j] = 0;}r ++; //消去一行时,继续判断当前行reshow();}}return line;}//一次消去行数为line时取得的分数function getScore(line){var score;switch(line){case 1:score = 10;break;case 2:score = 30;break;case 3:score = 60;break;case 4:score = 100;break;default:score = 0;break;}return score;}//更改级别。第一次升级需要1000分,以后每一次升级所需分数都比上一次多1000分function changeLevel(){var n = sum(level);if(level == 20) //最高20级{return;}if(score / 1000 > n){level += 1;changeSpeed();//升级后,方块下落的速度需要发生变化,所以清除旧的计时器,再新建一个clearInterval(taskTimer);taskTimer = setInterval("down();", period);document.getElementById("level").innerHTML = level;}}//求1+2+3+...+n的结果function sum(n){if(n <= 1){return 1;}return n + sum(n - 1);}//修改方块下落的速度function changeSpeed(){//每升一级,方块下落的时间间隔减少40毫秒period = 1200 - 40 * level;}function gameOver(){clearInterval(taskTimer);clearInterval(updateTimer);alert("您的得分是:" + score + "!\n游戏结束!");initBody();if(confirm("您想重新挑战吗?")){start();}}//更新显示区域的时间function updateTime(){var runTimeLabel = document.getElementById("runTime");runTime ++;runTimeLabel.innerHTML = formatTime(runTime);}//格式化时间function formatTime(time){var m = Math.floor(time / 60); //分钟var s = time % 60; //秒数var str = "";if(m < 10){str = "0";}str += m + ":";if(s < 10){str += "0";}str += s;return str;}function keyEvent(e) {if(navigator.appName == "Microsoft Internet Explorer"){//var keycode = event.keyCode;//var realkey = String.fromCharCode(event.keyCode);//alert("按键码: " + keycode + " 字符: " + realkey);keyAction(event.keyCode);}else{//alert(e.which);keyAction(e.which);}}function keyAction(key){switch(key){case 32: //向下快速移动quickDown();break;case 37: //向左移动left();break;case 38: //向上移动up();break;case 39: //向右移动right();break;case 40: //向下移动down();break;}}//开始游戏function start(){changeButtonStatus();if(gameStatus == "offDuty"){gameStatus = "run";moveTetromino2screen();}taskTimer = setInterval("down()", period);updateTimer = setInterval("updateTime()", 1000);}//暂停游戏function pause(){var btnpause = document.getElementById("btnpause");if(btnpause.value=="暂停"){btnpause.value = "恢复";clearInterval(taskTimer);clearInterval(updateTimer);}else{btnpause.value = "暂停";taskTimer = setInterval("down();", period);updateTimer = setInterval("updateTime()", 1000);}}//结束游戏function end(){if(confirm("你确定要结束游戏吗?")){clearInterval(taskTimer);clearInterval(updateTimer);initBody();}}//更改按钮状态function initButtonStatus(){document.getElementById("btnstart").disabled = false;document.getElementById("btnpause").disabled = true;document.getElementById("btnpause").value = "暂停";document.getElementById("btnend").disabled = true;document.getElementsByName("lineVisible")[0].disabled = false;document.getElementsByName("lineVisible")[0].checked = "checked";document.getElementsByName("lineVisible")[1].disabled = false;document.getElementsByName("randomTetrominoColor")[0].disabled = false;document.getElementsByName("randomTetrominoColor")[1].disabled = false;document.getElementsByName("randomTetrominoColor")[1].checked = "checked";document.getElementsByName("randomBottomColor")[0].disabled = false;document.getElementsByName("randomBottomColor")[1].disabled = false;document.getElementsByName("randomBottomColor")[1].checked = "checked";if(document.getElementsByName("randomTetrominoColor")[0].checked){randomTetrominoColor = true;}else{randomTetrominoColor = false;}if(document.getElementsByName("randomBottomColor")[0].checked){randomBottomColor = true;}else{randomBottomColor = false;}}//初始化按钮状态function changeButtonStatus(){document.getElementById("btnstart").disabled = true;document.getElementById("btnpause").disabled = false;document.getElementById("btnend").disabled = false;document.getElementsByName("lineVisible")[0].disabled = true;document.getElementsByName("lineVisible")[1].disabled = true;document.getElementsByName("randomTetrominoColor")[0].disabled = true;document.getElementsByName("randomTetrominoColor")[1].disabled = true;document.getElementsByName("randomBottomColor")[0].disabled = true;document.getElementsByName("randomBottomColor")[1].disabled = true;}//设置方块颜色的生成方式(即是否随机)function setRTColor(flag){randomTetrominoColor = flag;}//设置底部方块颜色的生成方式(即是否随机)function setRBColor(flag){randomBottomColor = flag;}//设置是否显示屏幕辅助线function setLineVisible(flag){lineVisible = flag;}initBody();document.onkeydown = keyEvent;</script>  </body></html>


读书人网 >JavaScript

热点推荐