html5实现的一个俄罗斯方块示例

haiyangyiba 3年前
   <p>示例简单,运行地址为:http://chendd.cn/demo/html/canvas/elsfk.html,得需要支持html5浏览器的环境。</p>    <p>实现的功能:<strong>方块旋转(W键)、自动下落、移动(ASD)、消行、快速下落(空格键)、下落阴影、游戏结束。</strong></p>    <p>为实现功能:消行时的计分、等级、以及不同等级的下落速度等。</p>    <p>学习了xiaoE的Java版本的俄罗斯方块后,自己动手使用html5的canvas实现的,</p>    <h2><span style="color:#B22222"><strong>参考效果图如下:</strong></span></h2>    <p><img alt="" src="https://simg.open-open.com/show/b40916d61e033dea84a16c08a48880e8.png"></p>    <h2><span style="color:#B22222"><strong>详细代码如下:</strong></span></h2>    <pre>  <code class="language-html">  <!DOCTYPE html>    <html>     <head>      <meta charset="utf-8">      <title>俄罗斯方块</title>      <style type="text/css">     /*整个画布*/          #tetris {      border: 6px solid grey;     }     /*游戏面板*/    </style>     </head>     <body>      <canvas id="tetris" width="565" height="576"></canvas>      <script type="text/javascript">     var canvas = document.getElementById("tetris");     var context = canvas.getContext("2d");     var padding = 6,      size = 32,      minX = 0,      maxX = 10,      minY = 0,      maxY = 18,      score = 0,      level = 1;     var gameMap = new Array(); //游戏地图,二维数组     var gameTimer;     initGameMap();     //绘制垂直线条     drawGrid();     var arrays = basicBlockType();     var blockIndex = getRandomIndex();     //随机画一个方块意思意思     var block = getPointByCode(blockIndex);     context.fillStyle = getBlockColorByIndex(blockIndex);     drawBlock(block);     /**        * 初始化游戏地图        */     function initGameMap() {      for (var i = 0; i < maxY; i++) {       var row = new Array();       for (var j = 0; j < maxX; j++) {        row[j] = false;       }       gameMap[i] = row;      }     }     /**        * 方块旋转        * 顺时针:        * A.x =O.y + O.x - B.y        * A.y =O.y - O.x + B.x        */     function round() {      //正方形的方块不响应旋转         if (blockIndex == 4) {       return;      }      //循环处理当前的方块,找新的旋转点      for (var i = 1; i < block.length; i++) {       var o = block[0];       var point = block[i];       //旋转后的位置不能与现有格子的方块冲突       var tempX = o.y + o.x - point.y;       var tempY = o.y - o.x + point.x;       if (isOverZone(tempX, tempY)) {        return; //不可旋转       }      }      clearBlock();      //可以旋转,设置新的旋转后的坐标      for (var i = 1; i < block.length; i++) {       var o = block[0];       var point = block[i];       //旋转后的位置不能与现有格子的方块冲突       var tempX = o.y + o.x - point.y;       var tempY = o.y - o.x + point.x;       block[i] = {        x: tempX,        y: tempY       };      }      drawBlock();     }     function moveDown() {            var overFlag = canOver();      if(overFlag){       //如果不能向下移动了,将当前的方块坐标载入地图       window.clearInterval(gameTimer);       add2GameMap();       //清除游戏区域内的不同颜色的格子,使用单一颜色重新绘制地图堆积物       redrawGameMap();       return;//游戏结束      }            var flag = moveTo(0, 1);      //如果可以移动,则继续移动      if (flag) {       return;      }      //如果不能向下移动了,将当前的方块坐标载入地图      add2GameMap();            //进行消行动作      clearLines();      //清除游戏区域内的不同颜色的格子,使用单一颜色重新绘制地图堆积物      redrawGameMap();      //如果不能向下移动,则继续下一个方块      nextBlock();     }          /**        * 消行动作,返回消除的行数        */     function clearLines() {      var clearRowList = new Array();      for (var i = 0; i < maxY; i++) {       var flag = true;       for (var j = 0; j < maxX; j++) {        if (gameMap[i][j] == false) {         flag = false;         break;        }       }       if (flag) {        clearRowList.push(i); //记录消除行号的索引       }      }      var clearRows = clearRowList.length;      //所谓的消行就是将待消除行的索引,下方所有的格子上移动      for (var x = 0; x < clearRows; x++) {       var index = clearRowList[x];       for (var i = index; i > 0; i--) {        for (var j = 0; j < maxX; j++) {         gameMap[i][j] = gameMap[i - 1][j];        }       }      }      if (clearRows > 0) {       for (var i = 0; i < maxY; i++) {        //此处可以限制满足相关条件的方块进行清除操作&& j < clearRowList[clearRows - 1]        for (var j = 0; j < maxX; j++) {         if (gameMap[i][j] == false) {          clearBlockByPoint(i, j);         }        }       }      }     }     /**        * 重绘游戏地图        */     function redrawGameMap() {      drawGrid();      for (var i = 0; i < maxY; i++) {       for (var j = 0; j < maxX; j++) {        if (gameMap[i][j]) {         roadBlock(j, i);        }       }      }     }     /**        * 打印阴影地图        */     function drawShadowBlock() {      var currentBlock = block;      var shadowPoints = getCanMoveDown();      if (shadowPoints != null && shadowPoints.length > 0) {       for (var i = 0; i < shadowPoints.length; i++) {        var point = shadowPoints[i];        if (point == null) {         continue;        }        var start = point.x * size;        var end = point.y * size;        context.fillStyle = "#abcdef";        context.fillRect(start, end, size, size);        context.strokeStyle = "black";        context.strokeRect(start, end, size, size);       }      }     }     /**        * 返回最多可移动到的坐标位置(统计总共可以下落多少步骤)        * @return最多可移动到的坐标位置        */     function getCanMoveDown() {      var nps = canMove(0, 1, block);      var last = null;      if (nps != null) {       last = new Array();       while ((nps = canMove(0, 1, nps)) != null) {        if (nps != null) {         last = nps;        }       }      }      return last;     }          function canOver(){      var flag = false;      for (var i = 0; i < block.length; i++) {       var point = block[i];       var x = point.x;       var y = point.y;       if(isOverZone(x , y)){        flag = true;        break;       }      }      return flag;     }          function drawLevelScore() {           }     /**        * 将不能移动的各种填充至地图        */     function add2GameMap() {      for (var i = 0; i < block.length; i++) {       var point = block[i];       var x = point.x;       var y = point.y;       var gameMapRow = gameMap[y]; //获取到地图的一行       gameMapRow[x] = true; //将此行中的某个格子标记为堆积物       gameMap[y] = gameMapRow; //再将行给设置回来      }     }     function moveLeft() {      moveTo(-1, 0);     }     function moveRight() {      moveTo(1, 0);     }     function quickDown() {      while (moveTo(0, 1));     }     function moveTo(moveX, moveY) {      var move = canMove(moveX, moveY, block); //判定是否可以移动      if (move == null) {       return false;      }      clearBlock();      for (var i = 0; i < block.length; i++) {       var point = block[i];       point.x = point.x + moveX;       point.y = point.y + moveY;      }      drawBlock();      return true;     }     /**        * 下一个方块        */     function nextBlock() {      blockIndex = getRandomIndex();      block = getPointByCode(blockIndex);      context.fillStyle = getBlockColorByIndex(blockIndex);      drawBlock();     }     document.onkeypress = function(evt) {      var key = window.event ? evt.keyCode : evt.which;      switch (key) {       case 119: //向上旋转 W        round();        break;       case 115: //向下移动  S        moveDown();        break;       case 97: //向左移动 A        moveLeft();        break;       case 100: //向右移动 D        moveRight();        break;       case 32: //空格键快速下落到底        quickDown();        break;      }     }     /**        * 判定是否可以移动        * @parammoveX 横向移动的个数        * @parammoveY 纵向移动的个数        */     function canMove(moveX, moveY, currentBlock) {      var flag = true;      var newPoints = new Array();      for (var i = 0; i < currentBlock.length; i++) {       var point = currentBlock[i];       var tempX = point.x + moveX;       var tempY = point.y + moveY;       if (isOverZone(tempX, tempY)) {        flag = false;        break;       }      }      if (flag) {       for (var i = 0; i < currentBlock.length; i++) {        var point = currentBlock[i];        var tempX = point.x + moveX;        var tempY = point.y + moveY;        newPoints[i] = {         x: tempX,         y: tempY        };       }       return newPoints;      }      return null;     }     /**        * 判定是否可以移动        * @paramx 预移动后的横坐标        * @paramy 预移动后的纵坐标        */     function isOverZone(x, y) {      return x < minX || x >= maxX || y < minY || y >= maxY || gameMap[y][x];     }     document.body.click();          gameTimer = window.setInterval(moveDown , 800);          /**        * 初始化方块的基础数据        */     function basicBlockType() {      var arrays = new Array();      arrays[0] = [{       x: 4,       y: 0      }, {       x: 3,       y: 0      }, {       x: 5,       y: 0      }, {       x: 6,       y: 0      }];      arrays[1] = [{       x: 4,       y: 0      }, {       x: 3,       y: 0      }, {       x: 5,       y: 0      }, {       x: 4,       y: 1      }];      arrays[2] = [{       x: 4,       y: 0      }, {       x: 3,       y: 0      }, {       x: 5,       y: 0      }, {       x: 3,       y: 1      }];      arrays[3] = [{       x: 4,       y: 0      }, {       x: 5,       y: 0      }, {       x: 3,       y: 1      }, {       x: 4,       y: 1      }];      arrays[4] = [{       x: 4,       y: 0      }, {       x: 5,       y: 0      }, {       x: 4,       y: 1      }, {       x: 5,       y: 1      }];      arrays[5] = [{       x: 4,       y: 0      }, {       x: 3,       y: 0      }, {       x: 5,       y: 0      }, {       x: 5,       y: 1      }];      arrays[6] = [{       x: 4,       y: 0      }, {       x: 3,       y: 0      }, {       x: 4,       y: 1      }, {       x: 5,       y: 1      }];      return arrays;     }     function basicBlockColor() {      return ["#A00000", "#A05000", "#A0A000", "#00A000", "#00A0A0", "#0000A0", "#A000A0"];     }     function getBlockColorByIndex(typeCodeIndex) {      var arrays = basicBlockColor();      return arrays[typeCodeIndex];     }     /**        * 根据编号返回指定编号的方块        * @paramtypeCodeIndex 方块编号索引        */     function getPointByCode(typeCodeIndex) {      var arrays = basicBlockType();      return arrays[typeCodeIndex];     }     /**        * 获取随即出现方块的范围值        * @paramlens 随机数的范围        */     function getRandomIndex() {      return parseInt(Math.random() * (arrays.length - 1), 10);     }     /**        * 绘制方块,按格子单个绘制        */     function drawBlock() {      drawGrid();      for (var i = 0; i < block.length; i++) {       var point = block[i];       var start = point.x * size;       var end = point.y * size;       context.fillStyle = getBlockColorByIndex(blockIndex);       context.fillRect(start, end, size, size);       context.strokeStyle = "black";       context.strokeRect(start, end, size, size);      }      drawShadowBlock();     }     /**        * 绘制障碍物        */     function roadBlock(x, y) {      context.fillStyle = "darkgray";      var start = x * size;      var end = y * size;      context.fillRect(start, end, size, size);     }     /**        * 绘制新的方块先清除之前的方块        */     function clearBlock() {      for (var i = 0; i < block.length; i++) {       var point = block[i];       var start = point.x * size;       var end = point.y * size;       context.clearRect(start, end, size, size);      }     }     /**        * 初始化一个新的行        */     function initGameMapRow() {      var array = new Array();      for (var i = 0; i < maxX; i++) {       array[i] = false;      }      return array;     }     /**        * 根据坐标清除指定格子的内容        * @paramx 横坐标        * @paramy 纵坐标        */     function clearBlockByPoint(x, y) {      var start = y * size;      var end = x * size;      context.clearRect(start, end, size, size);     }     /**        * 清掉所有位置的空白格的绘图        */     function clearAllNullPoint() {      for (var i = 0; i < maxY; i++) {       for (var j = 0; j < maxX; j++) {        if (gameMap[i][j] == false) {         clearBlockByPoint(i, j);        }       }      }     }     /**        * 绘制网格线        * @paramcontext 绘图对象        */     function drawGrid() {      clearAllNullPoint(); //清除掉当前方块下落位置造成的阴影      context.strokeStyle = "grey"; //画笔颜色      for (var i = 0; i <= maxX; i++) {       var start = i * size;       var end = start + size;       context.beginPath();       context.moveTo(start, 0);       context.lineTo(size * i, size * maxY);       context.stroke();       context.closePath();      }      //绘制水平线条      for (var i = 0; i <= maxY; i++) {       var start = i * size;       var end = start + size;       context.beginPath();       context.moveTo(0, size * i);       context.lineTo(size * maxX, size * i);       context.stroke();       context.closePath();      }     }    </script>     </body>    </html></code></pre>    <p> </p>