HTML5的俄罗斯方块

    xiaoxiao2021-03-26  24

    运用HTML5/CSS3/JavaScript开发一个小游戏:俄罗斯方块。 三个文件:tetris.html、tetris.css、tetris.js (完整的js代码在文末)

    程序思路框架&相关函数调用

    1、window.onload();//文档加载完成之后就会触发该事件

    (类似一个入口)

    Created with Raphaël 2.1.0 window.onload(); createCanvas(a,b,c,d);//创建canvas组件 drawBlock();// 绘制俄罗斯方块的状态 initBlock();//定义正在掉落的方块组合 setInterval(moveDown()),//周期性调用moveDown()直到游戏结束 游戏结束
    2、 window.onkeydown();// 为窗口的按键事件绑定事件监听器

    3、 moveDown();//处理掉落方块组合
    Created with Raphaël 2.1.0 moveDown(); lineFull();//判断是否有一行已满 initBlock();

    一、 开发游戏界面

    html页面代码:tetris.html <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html"; charset="utf-8"> <title>俄罗斯方块</title> <script type="text/javascript" src="tetris.js"></script> <link rel="stylesheet" type="text/css" href="tetris.css"> </head> <body id="body"> <h3 >俄罗斯方块</h3> <div class="blue">   <div class="speedscore"> 速度:<span id="curSpeedEle"></span> 当前积分:<span id="curScoreEle"></span> </div> <div class="maxscore"> 最高积分:<span id="maxScoreEle"></span> </div> </div> </body> </html>

    2 . css页面代码:tetris.css

    @font-face{ font-family:tmb; src:url("DS-DIGIB.TTF") format("TrueType"); } div{ font-size: 15pt; padding-bottom: 4px; } span{ font-family: tmb; font-size: 15pt; color:green; } body{ background-color: #F0B4DB; } .blue{ width: 400px; border: 1px solid black; background-color: #99FCFF; } .speedscore{ float: left; } .maxscore{ float: right; }

    3 . js页面代码:tetris.js

    //界面开发 var TETRIS_ROWS = 20; //20行 var TETRIS_COLS = 14; //14列 var CELL_SIZE = 25; //一个格子大小 var tetris_canvas; var tetris_ctx; //定义了一个函数,需要四个参数 var createCanvas = function(rows ,cols ,cellWidth ,cellHeight) //定义了一个函数,需要四个参数 { //新建canvas元素的tetris_canvas对象 //document.createElement()与document.appendChild()联用 //在这里不用tetris_canvas = document.getElementById("canvas"); //getElementById方法刷新页面不能继续游戏而是重新开始游戏 tetris_canvas = document.createElement("canvas"); //设置canvas组件的高度、宽度 tetris_canvas.width = cols * cellWidth; tetris_canvas.height = rows * cellHeight; //设置组件边框 tetris_canvas.style.border="1px solid black"; //获取canvas上的绘图API //获取canvas上绘图的CanvasRenderingContext2D对象 tetris_ctx=tetris_canvas.getContext('2d'); //开始创建路径 tetris_ctx.beginPath(); //绘制横向网格对应的路径,画网格里面的横线 for (var i=1; i < TETRIS_ROWS ; i++) { tetris_ctx.moveTo(0 , i * CELL_SIZE); //把当前路径结束点移动到(0 , i * CELL_SIZE),比如刚开始(0,a) tetris_ctx.lineTo(TETRIS_COLS * CELL_SIZE , i * CELL_SIZE); //从当前结束点连接到(TETRIS_COLS * CELL_SIZE , i * CELL_SIZE),比如从(0,a)到(14*a,0) } //绘制竖向网格对应的路径 for (var i=1; i < TETRIS_COLS ; i++) { tetris_ctx.moveTo(i * CELL_SIZE , 0); tetris_ctx.lineTo(i * CELL_SIZE , TETRIS_ROWS * CELL_SIZE); } tetris_ctx.closePath();//关闭前面定义的路径 //设置笔触颜色,绘制路径填充风格,当前为纯色;fillStyle是填充路径填充风格 tetris_ctx.strokeStyle="#314478"; //设置线条粗细 tetris_ctx.lineWidth=1; //绘制线条 tetris_ctx.stroke(); }

    二、数据模型

    //数据模型 var NO_BLOCK = 0; var currentFall; colors = ["#fff", "#f00" , "#0f0" , "#00f" , "#c60" , "#f0f" , "#0ff" , "#609"]; //该数组用于记录底下已经固定的方块,初始化为没有方块 var tetris_status=[]; for (var i = 0; i < TETRIS_ROWS; i++) { tetris_status[i]=[]; for (var j = 0; j < TETRIS_COLS; j++) { tetris_status[i][j] = NO_BLOCK; } } //定义几种可能出现的方块组合,相当于一个二维数组blockArr[7][4] var blockArr = [ [ {x:TETRIS_COLS/2-1,y:0,color:1}, //1. Z形方块组合 {x:TETRIS_COLS/2,y:0,color:1}, {x:TETRIS_COLS/2,y:1,color:1}, {x:TETRIS_COLS/2+1,y:1,color:1} ], [ {x:TETRIS_COLS/2+1,y:0,color:2}, //2. 反Z形方块组合 {x:TETRIS_COLS/2,y:0,color:2}, {x:TETRIS_COLS/2,y:1,color:2}, {x:TETRIS_COLS/2-1,y:1,color:2} ], [ {x:TETRIS_COLS/2-1,y:0,color:3}, //3. 田形方块组合 {x:TETRIS_COLS/2,y:0,color:3}, {x:TETRIS_COLS/2-1,y:1,color:3}, {x:TETRIS_COLS/2,y:1,color:3} ], [ {x:TETRIS_COLS/2,y:0,color:4}, //4. L形方块组合 {x:TETRIS_COLS/2,y:1,color:4}, {x:TETRIS_COLS/2,y:2,color:4}, {x:TETRIS_COLS/2+1,y:2,color:4} ], [ {x:TETRIS_COLS/2,y:0,color:5}, //5. J形方块组合 {x:TETRIS_COLS/2,y:1,color:5}, {x:TETRIS_COLS/2,y:2,color:5}, {x:TETRIS_COLS/2-1,y:2,color:5} ], [ {x:TETRIS_COLS/2,y:0,color:6}, //6. 竖形方块组合 {x:TETRIS_COLS/2,y:1,color:6}, {x:TETRIS_COLS/2,y:2,color:6}, {x:TETRIS_COLS/2,y:3,color:6} ], [ {x:TETRIS_COLS/2,y:0,color:7}, //7. 凸形方块组合 {x:TETRIS_COLS/2-1,y:1,color:7}, {x:TETRIS_COLS/2,y:1,color:7}, {x:TETRIS_COLS/2+1,y:1,color:7} ] ] //定义正在掉落的方块 var initBlock =function() { //随机生成正在掉落的方块(0-6) //math.floor(x)返回小于参数x的最大整数,即对浮点数向下取整. //Math.random()是令系统随机选取大于等于 0.0 且小于 1.0 的伪随机 double 值 var rand = Math.floor (Math.random() * blockArr.length); //第rand+1种方块组合,每种组合四个方块组成, currentFall = [ {x;blockArr[rand][0].x, y: blockArr[rand][0].y, color: blockArr[rand][0].color}, {x;blockArr[rand][1].x, y: blockArr[rand][1].y, color: blockArr[rand][1].color}, {x;blockArr[rand][2].x, y: blockArr[rand][2].y, color: blockArr[rand][2].color}, {x;blockArr[rand][3].x, y: blockArr[rand][3].y, color: blockArr[rand][3].color} ]; } //注意:tetris_status[y][x],因为二维数组竖直方向为i(行数)=纵坐标y,横向为j(列数)=横坐标x

    三、游戏逻辑

    //游戏逻辑 var curScore = 0; //当前积分值 var curSpeed = 1; //当前速度 var maxScore = 0; //最大积分值 var curScoreEle , curSpeedEle , maxScoreEle; var curTimer;//setInterval() 返回的ID值,curTimer = setInterval("moveDown();" , 500 / curSpeed); var isPlaying = true; //判断游戏是否结束 //处理掉落 var moveDown = function() { // 判断是否能掉落的标志 var canDown = true; // 遍历正在掉落的组合的每一个方块,判断是否能掉落 for (var i = 0 ; i < currentFall.length ; i++) { // 已经到了底部 if(currentFall[i].y >= TETRIS_ROWS - 1) { canDown = false; break; } // 下一格已经有方块 if(tetris_status[currentFall[i].y + 1][currentFall[i].x] != NO_BLOCK) { canDown = false; break; } } // 如果可以掉落 if(canDown) { // 首先将正在掉落的组合的每一格子涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { //cur代表组合的每一个格子(0 1 2 3) var cur = currentFall[i]; // 填充白色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 fillRect(x ,y ,width, height) tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } // 遍历组合每一个格子,下落一格,y坐标+1 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; cur.y ++; } // 下落后涂色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充颜色,colors[]在数据模型定义了数组 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } } // 若不能下落 else { // ±éÀúÿ¸ö·½¿é, °Ñÿ¸ö·½¿éµÄÖµ¼Ç¼µ½tetris_statusÊý×éÖÐ for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 如果有方块已经到了最上面,表明输了 if(cur.y < 2) { localStorage.removeItem("curScore"); // 清空Local Storage当前积分值 localStorage.removeItem("tetris_status"); // 清空Local Storage当前游戏状态 localStorage.removeItem("curSpeed"); // 清空Local Storage当前速度 if(confirm("您已经输了,是否参与排名")) //confirm对话框提示游戏结束 { // 读取Local Storage里面maxsScore记录 maxScore = localStorage.getItem("maxScore"); maxScore = maxScore == null ? 0 : maxScore ; // 如果当前积分大于localStorage最高积分 if(curScore >= maxScore) { // 记录最高分 localStorage.setItem("maxScore" , curScore); } } // 游戏结束 isPlaying = false; // 清楚计时器——该控制器控制方块组合不断向下掉落 clearInterval(curTimer); return; } // 还没有输,把把每个方块当前所在位置赋为当前方块颜色值 tetris_status[cur.y][cur.x] = cur.color; } lineFull(); // 判断是否有可消除的行 // 使用Local Storage记录游戏状态 localStorage.setItem("tetris_status" , JSON.stringify(tetris_status)); //开始新组块 initBlock(); } } //判断是否有一行已满 var lineFull =function() { // 遍历每一行 for (var i = 0; i < TETRIS_ROWS ; i++ ) { var flag = true; // 用flag标志是否有行已经满了 // 遍历当前行每个单元格 for (var j = 0 ; j < TETRIS_COLS ; j++ ) { if(tetris_status[i][j] == NO_BLOCK) { flag = false; //有空格,跳出该行循环 break; } } // flag = true,该行已满 if(flag) { // 当前积分+100 curScoreEle.innerHTML = curScore+= 100; // 记录当前积分 localStorage.setItem("curScore" , curScore); // 如果当前积分到达升级的极限 if( curScore >= curSpeed * curSpeed * 500) { curSpeedEle.innerHTML = curSpeed += 1; // 记录当前速度 localStorage.setItem("curSpeed" , curSpeed); clearInterval(curTimer); curTimer = setInterval("moveDown();" , 500 / curSpeed); /*setInterval() 方法会不停地调用函数, 直到 clearInterval() 被调用或窗口被关闭。 由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。*/ } // 当前行的所有方块下移一行 for (var k = i ; k > 0 ; k--) { for (var l = 0; l < TETRIS_COLS ; l++ ) { tetris_status[k][l] =tetris_status[k-1][l]; } } // 重新绘制一遍方块 drawBlock(); } } } // 绘制俄罗斯方块的状态 var drawBlock = function() { for (var i = 0; i < TETRIS_ROWS ; i++ ) { for (var j = 0; j < TETRIS_COLS ; j++ ) { // 有方块的地方绘制颜色 if(tetris_status[i][j] != NO_BLOCK) { // 填充颜色 tetris_ctx.fillStyle = colors[tetris_status[i][j]]; // 绘制矩形 tetris_ctx.fillRect(j * CELL_SIZE + 1 , i * CELL_SIZE + 1, CELL_SIZE - 2 , CELL_SIZE - 2); } // 没有方块地方绘制白色 else { // 填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(j * CELL_SIZE + 1 , i * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } } } } var moveLeft = function() { // 是否能左移标志 var canLeft = true; for (var i = 0 ; i < currentFall.length ; i++) { // 如果到了最左边不能移动 if(currentFall[i].x <= 0) { canLeft = false; break; } // 左边位置已经有方块 if (tetris_status[currentFall[i].y][currentFall[i].x - 1] != NO_BLOCK) { canLeft = false; break; } } // 如果能左移 if(canLeft) { // 左移前每个方块背景涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE +1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE - 2); } // 左移组合 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; cur.x --; } // 左移后方块涂成相应颜色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充颜色 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1, CELL_SIZE - 2 , CELL_SIZE - 2); } } } // 右移功能 var moveRight = function() { // 是否能右移标志 var canRight = true; for (var i = 0 ; i < currentFall.length ; i++) { // 到达最右边,不能右移 if(currentFall[i].x >= TETRIS_COLS - 1) { canRight = false; break; } // 右边位置已经有方块,不能右移 if (tetris_status[currentFall[i].y][currentFall[i].x + 1] != NO_BLOCK) { canRight = false; break; } } // 如果能右移 if(canRight) { // 右移前每个方块的背景涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充白色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } // 右移正在掉落的方块组合坐标 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; cur.x ++; } // ½«ÓÒÒÆºóµÄÿ¸ö·½¿éµÄ±³¾°É«Í¿³É¸÷·½¿é¶ÔÓ¦µÄÑÕÉ« for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充方块 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE -2); } } } // 旋转方块函数 var rotate = function() { // 定义是否能旋转的标志 var canRotate = true; for (var i = 0 ; i < currentFall.length ; i++) { var preX = currentFall[i].x; var preY = currentFall[i].y; // 始终以第三个方块作为旋转中心 // i == 2说明是旋转中心 if(i != 2) { // 计算旋转后方块坐标 var afterRotateX = currentFall[2].x + preY - currentFall[2].y; var afterRotateY = currentFall[2].y + currentFall[2].x - preX; // 如果旋转后的位置已经有方块,不能旋转 if(tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK) { canRotate = false; break; } // 旋转后坐标超出最左边界 if(afterRotateX < 0 || tetris_status[afterRotateY - 1][afterRotateX] != NO_BLOCK) { moveRight(); afterRotateX = currentFall[2].x + preY - currentFall[2].y; afterRotateY = currentFall[2].y + currentFall[2].x - preX; break; } if(afterRotateX < 0 || tetris_status[afterRotateY-1][afterRotateX] != NO_BLOCK) { moveRight(); break; } // 旋转后坐标超出最右边界 if(afterRotateX >= TETRIS_COLS - 1 || tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK) { moveLeft(); afterRotateX = currentFall[2].x + preY - currentFall[2].y; afterRotateY = currentFall[2].y + currentFall[2].x - preX; break; } if(afterRotateX >= TETRIS_COLS - 1 || tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK) { moveLeft(); break; } } } // 能旋转 if(canRotate) { // 旋转前背景涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE - 2); } for (var i = 0 ; i < currentFall.length ; i++) { var preX = currentFall[i].x; var preY = currentFall[i].y; // 始终以第三个方块作为旋转中心 // i == 2说明是旋转中心 if(i != 2) { currentFall[i].x = currentFall[2].x + preY - currentFall[2].y; currentFall[i].y = currentFall[2].y + currentFall[2].x - preX; } } // 旋转后每个方块的背景涂成各方块对应的颜色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充颜色 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE - 2); } } }

    四、给键盘事件绑定事件监听器

    window.focus(); // 为窗口的按键事件绑定事件监听器 window.onkeydown = function(evt) { //evt.keyCode 是指触发这个键盘事件的键盘码 switch(evt.keyCode) { // 按了“向下”箭头 case 40: if(!isPlaying) return; moveDown(); break; // 按了“向左”箭头 case 37: if(!isPlaying) return; moveLeft(); break; // 按了“向右”箭头 case 39: if(!isPlaying) return; moveRight(); break; // 按了“向上”箭头 case 38: if(!isPlaying) return; rotate(); break; } }

    五、初始化游戏状态

    //初始化游戏状态 window.onload = function() { //创建canvas组件 createCanvas(TETRIS_ROWS , TETRIS_COLS , CELL_SIZE , CELL_SIZE); document.body.appendChild(tetris_canvas); curScoreEle = document.getElementById("curScoreEle"); curSpeedEle = document.getElementById("curSpeedEle"); maxScoreEle = document.getElementById("maxScoreEle"); // 读取Local Storage里的tetris_status记录 var tmpStatus = localStorage.getItem("tetris_status"); tetris_status = tmpStatus == null ? tetris_status : JSON.parse(tmpStatus); // 把方块绘制出来 drawBlock(); // 读取Local Storage里的curScore记录 curScore = localStorage.getItem("curScore"); curScore = curScore == null ? 0 : parseInt(curScore); curScoreEle.innerHTML = curScore; // 读取Local Storage里的maxScore记录 maxScore = localStorage.getItem("maxScore"); maxScore = maxScore == null ? 0 : parseInt(maxScore); maxScoreEle.innerHTML = maxScore; // 读取Local Storage里的curSpeed记录 curSpeed = localStorage.getItem("curSpeed"); curSpeed = curSpeed == null ? 1 : parseInt(curSpeed); curSpeedEle.innerHTML = curSpeed; // 初始化正在掉落的方块 initBlock(); // 控制每隔一段时间执行一次“下落” curTimer = setInterval("moveDown();" , 500 / curSpeed); }

    从上面代码可以看出,俄罗斯方块游戏开始时需要完成如下事情: 1、 调用createCanvas创建canvas组件 2、 读取Local Storage 记录的已有方块的状态 3、 读取Local Storage 记录的当前积分数据 4、 读取Local Storage 记录的当前速度数据 5、 读取Local Storage 记录的最高积分数据 6、 初始化正在掉落的方块组合 7、 启动计时器,控制方块掉落

    ※js完整代码

    var TETRIS_ROWS = 20; //20行 var TETRIS_COLS = 14; //14列 var CELL_SIZE = 25; //一个格子大小 var tetris_canvas; //canvas元素的tetris_canvas对象,在createCanvas var tetris_ctx; //CanvasRenderingContext2D对象,在createCanvas var curScore = 0; //当前积分值 var curSpeed = 1; //当前速度 var maxScore = 0; //最大积分值 var curScoreEle , curSpeedEle , maxScoreEle; //引用拥有制定ID的第一个对象,在window.onload var curTimer;//setInterval() 返回的ID值,curTimer = setInterval("moveDown();" , 500 / curSpeed); var isPlaying = true; //判断游戏是否结束 var NO_BLOCK = 0; var currentFall; colors = ["#fff", "#f00" , "#0f0" , "#00f" , "#c60" , "#f0f" , "#0ff" , "#609"]; //该数组用于记录底下已经固定的方块,初始化为没有方块 var tetris_status=[]; for (var i = 0; i < TETRIS_ROWS; i++) { tetris_status[i]=[]; for (var j = 0; j < TETRIS_COLS; j++) { tetris_status[i][j] = NO_BLOCK; } } //定义几种可能出现的方块组合,相当于一个二维数组blockArr[7][4] var blockArr = [ [ {x:TETRIS_COLS/2-1,y:0,color:1}, //1. Z形方块组合 {x:TETRIS_COLS/2,y:0,color:1}, {x:TETRIS_COLS/2,y:1,color:1}, {x:TETRIS_COLS/2+1,y:1,color:1} ], [ {x:TETRIS_COLS/2+1,y:0,color:2}, //2. 反Z形方块组合 {x:TETRIS_COLS/2,y:0,color:2}, {x:TETRIS_COLS/2,y:1,color:2}, {x:TETRIS_COLS/2-1,y:1,color:2} ], [ {x:TETRIS_COLS/2-1,y:0,color:3}, //3. 田形方块组合 {x:TETRIS_COLS/2,y:0,color:3}, {x:TETRIS_COLS/2-1,y:1,color:3}, {x:TETRIS_COLS/2,y:1,color:3} ], [ {x:TETRIS_COLS/2,y:0,color:4}, //4. L形方块组合 {x:TETRIS_COLS/2,y:1,color:4}, {x:TETRIS_COLS/2,y:2,color:4}, {x:TETRIS_COLS/2+1,y:2,color:4} ], [ {x:TETRIS_COLS/2,y:0,color:5}, //5. J形方块组合 {x:TETRIS_COLS/2,y:1,color:5}, {x:TETRIS_COLS/2,y:2,color:5}, {x:TETRIS_COLS/2-1,y:2,color:5} ], [ {x:TETRIS_COLS/2,y:0,color:6}, //6. 竖形方块组合 {x:TETRIS_COLS/2,y:1,color:6}, {x:TETRIS_COLS/2,y:2,color:6}, {x:TETRIS_COLS/2,y:3,color:6} ], [ {x:TETRIS_COLS/2,y:0,color:7}, //7. 凸形方块组合 {x:TETRIS_COLS/2-1,y:1,color:7}, {x:TETRIS_COLS/2,y:1,color:7}, {x:TETRIS_COLS/2+1,y:1,color:7} ] ] //定义正在掉落的方块 var initBlock =function() { //随机生成正在掉落的方块(0-6) //math.floor(x)返回小于参数x的最大整数,即对浮点数向下取整. //Math.random()是令系统随机选取大于等于 0.0 且小于 1.0 的伪随机 double 值 var rand = Math.floor (Math.random() * blockArr.length); //第rand+1种方块组合,每种组合四个方块组成, currentFall = [ {x:blockArr[rand][0].x, y: blockArr[rand][0].y, color: blockArr[rand][0].color}, {x:blockArr[rand][1].x, y: blockArr[rand][1].y, color: blockArr[rand][1].color}, {x:blockArr[rand][2].x, y: blockArr[rand][2].y, color: blockArr[rand][2].color}, {x:blockArr[rand][3].x, y: blockArr[rand][3].y, color: blockArr[rand][3].color} ]; } //注意:tetris_status[y][x],因为二维数组竖直方向为i(行数)=纵坐标y,横向为j(列数)=横坐标x var createCanvas = function(rows ,cols ,cellWidth ,cellHeight) //定义了一个函数,需要四个参数 { //新建canvas元素的tetris_canvas对象 //document.createElement()与document.appendChild()联用 //tetris_canvas = document.getElementById("canvas");这种方法刷新页面不能继续游戏而是重新开始游戏 tetris_canvas = document.createElement("canvas"); //设置canvas组件的高度、宽度 tetris_canvas.width = cols * cellWidth; tetris_canvas.height = rows * cellHeight; //设置组件边框 tetris_canvas.style.border="1px solid black"; //获取canvas上的绘图API //获取canvas上绘图的CanvasRenderingContext2D对象 tetris_ctx=tetris_canvas.getContext('2d'); //开始创建路径 tetris_ctx.beginPath(); //绘制横向网格对应的路径,画网格里面的横线 for (var i=1; i < TETRIS_ROWS ; i++) { tetris_ctx.moveTo(0 , i * CELL_SIZE); //把当前路径结束点移动到(0 , i * CELL_SIZE),比如刚开始(0,a) tetris_ctx.lineTo(TETRIS_COLS * CELL_SIZE , i * CELL_SIZE); //从当前结束点连接到(TETRIS_COLS * CELL_SIZE , i * CELL_SIZE),比如从(0,0)到(14*a,0);(0,a)-(14a,a) } //绘制竖向网格对应的路径 for (var i=1; i < TETRIS_COLS ; i++) { tetris_ctx.moveTo(i * CELL_SIZE , 0); tetris_ctx.lineTo(i * CELL_SIZE , TETRIS_ROWS * CELL_SIZE); } tetris_ctx.closePath();//关闭前面定义的路径 //设置笔触颜色,绘制路径填充风格,当前为纯色;fillStyle是填充路径填充风格 tetris_ctx.strokeStyle="#314478"; //设置线条粗细 tetris_ctx.lineWidth=1; //绘制线条 tetris_ctx.stroke(); } //判断是否有一行已满 var lineFull =function() { // 遍历每一行 for (var i = 0; i < TETRIS_ROWS ; i++ ) { var flag = true; // 用flag标志是否有行已经满了 // 遍历当前行每个单元格 for (var j = 0 ; j < TETRIS_COLS ; j++ ) { if(tetris_status[i][j] == NO_BLOCK) { flag = false; //有空格,跳出该行循环 break; } } // flag = true,该行已满 if(flag) { // 当前积分+100 curScoreEle.innerHTML = curScore+= 100; // 记录当前积分 localStorage.setItem("curScore" , curScore); // 如果当前积分到达升级的极限 if( curScore >= curSpeed * curSpeed * 500) { curSpeedEle.innerHTML = curSpeed += 1; // 记录当前速度 localStorage.setItem("curSpeed" , curSpeed); clearInterval(curTimer); curTimer = setInterval("moveDown();" , 500 / curSpeed); //etInterval() 方法会不停地调用函数, //直到 clearInterval() 被调用或窗口被关闭。 //由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。 } // 当前行的所有方块下移一行 for (var k = i ; k > 0 ; k--) { for (var l = 0; l < TETRIS_COLS ; l++ ) { tetris_status[k][l] =tetris_status[k-1][l]; } } // 重新绘制一遍方块 drawBlock(); } } } //处理掉落方块组合 var moveDown = function() { // 判断是否能掉落的标志 var canDown = true; // 遍历正在掉落的组合的每一个方块,判断是否能掉落 for (var i = 0 ; i < currentFall.length ; i++) { // 已经到了底部 if(currentFall[i].y >= TETRIS_ROWS - 1) { canDown = false; break; } // 下一格已经有方块 if(tetris_status[currentFall[i].y + 1][currentFall[i].x] != NO_BLOCK) { canDown = false; break; } } // 如果可以掉落 if(canDown) { // 首先将正在掉落的组合的每一格子涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { //cur代表组合的每一个格子(0 1 2 3) var cur = currentFall[i]; // 填充白色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 fillRect(x ,y ,width, height) tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } // 遍历组合每一个格子,下落一格,y坐标+1 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; cur.y ++; } // 下落后涂色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充颜色,colors[]在数据模型定义了数组 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } } // 若不能下落 else { // ±éÀúÿ¸ö·½¿é, °Ñÿ¸ö·½¿éµÄÖµ¼Ç¼µ½tetris_statusÊý×éÖÐ for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 如果有方块已经到了最上面,表明输了 if(cur.y < 2) { localStorage.removeItem("curScore"); // 清空Local Storage当前积分值 localStorage.removeItem("tetris_status"); // 清空Local Storage当前游戏状态 localStorage.removeItem("curSpeed"); // 清空Local Storage当前速度 if(confirm("您已经输了,是否参与排名")) //confirm对话框提示游戏结束 { // 读取Local Storage里面maxsScore记录 maxScore = localStorage.getItem("maxScore"); maxScore = maxScore == null ? 0 : maxScore ; // 如果当前积分大于localStorage最高积分 if(curScore >= maxScore) { // 记录最高分 localStorage.setItem("maxScore" , curScore); } } // 游戏结束 isPlaying = false; // 清楚计时器——该控制器控制方块组合不断向下掉落 clearInterval(curTimer); return; } // 还没有输,把把每个方块当前所在位置赋为当前方块颜色值 tetris_status[cur.y][cur.x] = cur.color; } lineFull(); // 判断是否有可消除的行 // 使用Local Storage记录游戏状态 localStorage.setItem("tetris_status" , JSON.stringify(tetris_status)); //开始新组块 initBlock(); } } // 绘制俄罗斯方块的状态 var drawBlock = function() { for (var i = 0; i < TETRIS_ROWS ; i++ ) { for (var j = 0; j < TETRIS_COLS ; j++ ) { // 有方块的地方绘制颜色 if(tetris_status[i][j] != NO_BLOCK) { // 填充颜色 tetris_ctx.fillStyle = colors[tetris_status[i][j]]; // 绘制矩形 tetris_ctx.fillRect(j * CELL_SIZE + 1 , i * CELL_SIZE + 1, CELL_SIZE - 2 , CELL_SIZE - 2); } // 没有方块地方绘制白色 else { // 填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(j * CELL_SIZE + 1 , i * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } } } } // 左移方块函数 var moveLeft = function() { // 是否能左移标志 var canLeft = true; for (var i = 0 ; i < currentFall.length ; i++) { // 如果到了最左边不能移动 if(currentFall[i].x <= 0) { canLeft = false; break; } // 左边位置已经有方块 if (tetris_status[currentFall[i].y][currentFall[i].x - 1] != NO_BLOCK) { canLeft = false; break; } } // 如果能左移 if(canLeft) { // 左移前每个方块背景涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE +1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE - 2); } // 左移组合 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; cur.x --; } // 左移后方块涂成相应颜色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充颜色 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1, CELL_SIZE - 2 , CELL_SIZE - 2); } } } // 右移方块函数 var moveRight = function() { // 是否能右移标志 var canRight = true; for (var i = 0 ; i < currentFall.length ; i++) { // 到达最右边,不能右移 if(currentFall[i].x >= TETRIS_COLS - 1) { canRight = false; break; } // 右边位置已经有方块,不能右移 if (tetris_status[currentFall[i].y][currentFall[i].x + 1] != NO_BLOCK) { canRight = false; break; } } // 如果能右移 if(canRight) { // 右移前每个方块的背景涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充白色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2 , CELL_SIZE - 2); } // 右移正在掉落的方块组合坐标 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; cur.x ++; } // ½«ÓÒÒÆºóµÄÿ¸ö·½¿éµÄ±³¾°É«Í¿³É¸÷·½¿é¶ÔÓ¦µÄÑÕÉ« for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充方块 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE -2); } } } // 旋转方块函数 var rotate = function() { // 定义是否能旋转的标志 var canRotate = true; for (var i = 0 ; i < currentFall.length ; i++) { var preX = currentFall[i].x; var preY = currentFall[i].y; // 始终以第三个方块作为旋转中心 // i == 2说明是旋转中心 if(i != 2) { // 计算旋转后方块坐标 var afterRotateX = currentFall[2].x + preY - currentFall[2].y; var afterRotateY = currentFall[2].y + currentFall[2].x - preX; // 如果旋转后的位置已经有方块,不能旋转 if(tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK) { canRotate = false; break; } // 旋转后坐标超出最左边界 if(afterRotateX < 0 || tetris_status[afterRotateY - 1][afterRotateX] != NO_BLOCK) { moveRight(); afterRotateX = currentFall[2].x + preY - currentFall[2].y; afterRotateY = currentFall[2].y + currentFall[2].x - preX; break; } if(afterRotateX < 0 || tetris_status[afterRotateY-1][afterRotateX] != NO_BLOCK) { moveRight(); break; } // 旋转后坐标超出最右边界 if(afterRotateX >= TETRIS_COLS - 1 || tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK) { moveLeft(); afterRotateX = currentFall[2].x + preY - currentFall[2].y; afterRotateY = currentFall[2].y + currentFall[2].x - preX; break; } if(afterRotateX >= TETRIS_COLS - 1 || tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK) { moveLeft(); break; } } } // 能旋转 if(canRotate) { // 旋转前背景涂成白色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 设置填充颜色 tetris_ctx.fillStyle = 'white'; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE - 2); } for (var i = 0 ; i < currentFall.length ; i++) { var preX = currentFall[i].x; var preY = currentFall[i].y; // 始终以第三个方块作为旋转中心 // i == 2说明是旋转中心 if(i != 2) { currentFall[i].x = currentFall[2].x + preY - currentFall[2].y; currentFall[i].y = currentFall[2].y + currentFall[2].x - preX; } } // 旋转后每个方块的背景涂成各方块对应的颜色 for (var i = 0 ; i < currentFall.length ; i++) { var cur = currentFall[i]; // 填充颜色 tetris_ctx.fillStyle = colors[cur.color]; // 绘制矩形 tetris_ctx.fillRect(cur.x * CELL_SIZE + 1 , cur.y * CELL_SIZE + 1 , CELL_SIZE - 2, CELL_SIZE - 2); } } } //窗口获取焦点(玩网页游戏需要在页面点击一下) window.focus(); // 为窗口的按键事件绑定事件监听器 window.onkeydown = function(evt) { //evt.keyCode 是指触发这个键盘事件的键盘码 switch(evt.keyCode) { // 按了“向下”箭头 case 40: if(!isPlaying) return; moveDown(); break; // 按了“向左”箭头 case 37: if(!isPlaying) return; moveLeft(); break; // 按了“向右”箭头 case 39: if(!isPlaying) return; moveRight(); break; // 按了“向上”箭头 case 38: if(!isPlaying) return; rotate(); break; } } //初始化游戏状态 window.onload = function() { //创建canvas组件 createCanvas(TETRIS_ROWS , TETRIS_COLS , CELL_SIZE , CELL_SIZE); document.body.appendChild(tetris_canvas); //getElementById() 方法可返回对拥有指定 ID 的第一个对象的引用。 curScoreEle = document.getElementById("curScoreEle"); curSpeedEle = document.getElementById("curSpeedEle"); maxScoreEle = document.getElementById("maxScoreEle"); // 读取Local Storage里的tetris_status记录 var tmpStatus = localStorage.getItem("tetris_status"); tetris_status = tmpStatus == null ? tetris_status : JSON.parse(tmpStatus); // 把方块绘制出来 drawBlock(); // 读取Local Storage里的curScore记录 curScore = localStorage.getItem("curScore"); curScore = curScore == null ? 0 : parseInt(curScore); curScoreEle.innerHTML = curScore; // 读取Local Storage里的maxScore记录 maxScore = localStorage.getItem("maxScore"); maxScore = maxScore == null ? 0 : parseInt(maxScore); maxScoreEle.innerHTML = maxScore; // 读取Local Storage里的curSpeed记录 curSpeed = localStorage.getItem("curSpeed"); curSpeed = curSpeed == null ? 1 : parseInt(curSpeed); curSpeedEle.innerHTML = curSpeed; // 初始化正在掉落的方块 initBlock(); // 控制每隔一段时间执行一次“下落” curTimer = setInterval("moveDown();" , 500 / curSpeed); }
    转载请注明原文地址: https://ju.6miu.com/read-600360.html

    最新回复(0)