JS实现俄罗斯方块

更新日期: 2019-05-17阅读: 2.4k标签: 游戏

《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。  下面使用js来实现俄罗斯方块

把以下代码保存成Tetris.html文件,使用Google或360浏览器打开  

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 4.0 Transitional//EN">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta name="charset-8"/>
  <title> new document </title>
</head>

<body>
  <div id="space"></div>
  <div id="debug" style="position:relative;top:-600px;left:330px"></div>
</body>

<script>
//定义按键
var KEY_LEFT = 37;
var KEY_RIGHT = 39;
var KEY_ROTATE = 38;
var KEY_ACCELERATE = 40;
var KEY_PAUSE = 13;
var KEY_ONE_STOP = 32;

//定义地图大小
var MAP_R = 18;
var MAP_C = 10;

//定义方块大小
var BLOCK_R = 4;
var BLOCK_C = 4;
//定义各种方块
var BLOCKS = [
//I
[
 [[1,1,1,1], 
  [0,0,0,0],
  [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,1], 
  [0,0,0,0],
  [0,0,0,0],
  [0,0,0,0]],
 [[0,1,0,0], 
  [0,1,0,0],
  [0,1,0,0],
  [0,1,0,0]]
],

//L
[
 [[1,0,0,0], 
  [1,1,1,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]],
 [[1,1,1,0], 
  [0,0,1,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]]
],

//J
[
 [[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]],
 [[1,0,0,0],
  [1,0,0,0],
  [1,1,0,0],
  [0,0,0,0]]
],

//O
[
 [[0,1,1,0], 
  [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,1,1,0], 
  [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]]
],

//S
[
 [[0,1,1,0],
  [1,1,0,0],
  [0,0,0,0],
  [0,0,0,0]],
 [[0,1,0,0],
  [0,1,1,0],
  [0,0,1,0],
  [0,0,0,0]],
 [[0,1,1,0],
  [1,1,0,0],
  [0,0,0,0],
  [0,0,0,0]],
 [[0,1,0,0],
  [0,1,1,0],
  [0,0,1,0],
  [0,0,0,0]]
],

//T
[
 [[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,1,0,0],
  [0,0,0,0]],
 [[0,1,0,0],
  [1,1,0,0],
  [0,1,0,0],
  [0,0,0,0]]
],

//Z
[
 [[1,1,0,0],
  [0,1,1,0],
  [0,0,0,0],
  [0,0,0,0]],
 [[0,1,0,0],
  [1,1,0,0],
  [1,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,0,0],
  [1,1,0,0],
  [1,0,0,0],
  [0,0,0,0]]
]
];

var map = [];
for(var r = 0; r < MAP_R; r++) {
  map.push([]);
  for(var c = 0; c < MAP_C; c++) {
    map[r][c] = {};
    map[r][c].b = 0;
  }
}

var colors = ['darkorange','darkviolet','mediumblue','red','lightseagreen','yellow','lime'];

var enableShadow = true;
var shadow = {};

var currR, currC; //方格当前在Space的左顶点位置
var currType; //当前正在落下方块的种类
var currDir = 0; //当前正在落下方块的方向
var pause = false;

//可以落下
function canFall(currR, currC) {
  for(var c = 0; c < BLOCK_C; c++)
    for(var r = BLOCK_R - 1; r >= 0; r--) {
      if(!BLOCKS[currType][currDir][r][c])
        continue;
      if(currR + r + 1 > MAP_R - 1)
        return false;
      if(map[currR + r + 1][currC + c].b)
        return false;
   }
  return true;
}

//找方块的投影坐标
function makeShadow() {
  shadow.r = currR;
  shadow.c = currC;
  while(canFall(shadow.r, shadow.c)) {
    shadow.r++;
  }
}

//落下状态设置到map
function fall(block) {
  for(var r = 0; r < BLOCK_R; r++)
    for(var c = 0; c < BLOCK_C; c++)
      if(BLOCKS[currType][currDir][r][c]) {
        map[currR + r][currC + c].b = BLOCKS[currType][currDir][r][c];
        map[currR + r][currC + c].c = currType;
      }
}

//可以左移
function canLeft() {
  for(var r = 0; r < BLOCK_R; r++)
    for(var c = 0; c < BLOCK_C; c++) {
      if(!BLOCKS[currType][currDir][r][c])
        continue;
      if(currC + c - 1 < 0)
        return false;
      if(map[currR + r][currC + c - 1].b)
        return false;
   }
  return true;
}

//可以右移
function canRight() {
  for(var r = 0; r < BLOCK_R; r++)
    for(var c = BLOCK_C - 1; c >= 0; c--) {
      if(!BLOCKS[currType][currDir][r][c])
        continue;
      if(currC + c + 1 > MAP_C - 1)
        return false;
      if(map[currR + r][currC + c + 1].b)
        return false;
   }
  return true;
}

//可以旋转
function canRotate() {
  return true;
}

//获得满行的行位置
function checkFullRows() {
  var rows = [];
  var full;
  for(var r = currR; r < MAP_R; r++) {
    full = true;
    for(var c = 0; full && c < MAP_C; c++)
      full = map[r][c].b;
    if(full)
      rows.push(r);
  }
  return rows;
}

function showPop(rows) {
  for(var i = 0; i < rows.length; i++)
    for(var c = 0; c < MAP_C; c++)
      $(rows[i] + '-' + c).style.backgroundColor = 'transparent';
}

//在map消除指定行位置的行
function popRows(rows) {
  for(var i = 0; i < rows.length; i++)
    for(var r = rows[i] - 1; r >= 0; r--)
      for(var c = 0; c < MAP_C; c++) {
        map[r + 1][c].b = map[r][c].b;
        map[r + 1][c].c = map[r][c].c;
      }
}

document.onkeydown = function(event) {
  var keyCode = window .event?event.keyCode:event.which;

  if(keyCode == KEY_LEFT ||  keyCode == KEY_RIGHT) {
    easeBlock();
    if(enableShadow)
      easeShadow();
    if(keyCode == KEY_LEFT) {
      if(canLeft())
        --currC;
    } else if(keyCode == KEY_RIGHT) {
      if(canRight())
        ++currC;
    }
    drawBlock();
    if(enableShadow) {
      makeShadow();
      drawShadow();
    }
  } else if(keyCode == KEY_ROTATE) {
    if(canRotate()) {
      easeBlock();
      if(enableShadow)
        easeShadow();
      currDir = [1,2,3,0][currDir];
      drawBlock();
      if(enableShadow) {
        makeShadow();
        drawShadow();
      }
    }
  } else if(keyCode == KEY_ACCELERATE) {
    loop();
  } else if(keyCode == KEY_PAUSE) {
    pause = !pause;
    if(pause)
      clearInterval(timer);
    else
      timer = setInterval(loop, 300);  
  } else if(keyCode == KEY_ONE_STOP) {
    easeBlock();
    makeShadow();
    currR = shadow.r;
    currC = shadow.c;
    drawBlock();  
  }
    
  //printMapState();
}

function nextBlock() {
  function randInt(n, m) {
    return Math.floor(Math.random() * (m - n)) + n;
  }

  currR = 0;
  currC = MAP_C / 2 - BLOCK_C / 2;
  currType = randInt(0, BLOCKS.length);
  currDir = randInt(0, 4);
}

function printMapState() {
  var debug = $('debug');
  var html = '';
  for(var r = 0; r < MAP_R; r++) {
    for(var c = 0; c < MAP_C; c++)
      html += map[r][c].b;
    html += '</br>';
  }
  debug.innerHTML = html;
}

onload = function() {
init();
nextBlock();
drawBlock();
makeShadow();
drawShadow();

timer = setInterval(loop, 300);
};

function loop() {
  if(canFall(currR, currC)) {
    easeBlock();
    ++currR;
    drawBlock();
  } else {
    fall();
    if(currR == 0) {
      drawBlock();
      clearInterval(timer);
      alert('Game Over');
      return;
    }
    var rows = checkFullRows();
    if(rows.length > 0) {
      showPop(rows);
      popRows(rows);
      setTimeout(function(){
        drawMap();
      }, 100);
    }
    if(enableShadow)
      easeShadow();
    nextBlock();
    drawBlock();
    if(enableShadow) {
      makeShadow();
      drawShadow();
    }
  }
}

function drawMap() {
  for(var r = 0; r < MAP_R; r++)
    for(var c = 0; c < MAP_C; c++) {
      var div = $(r + '-' + c);
      if(map[r][c].b) {
        div.style.backgroundColor = colors[map[r][c].c];
      } else {
        div.style.backgroundColor = 'transparent';
      }
    }
}

function drawBlock() {
  for(var r = 0; r < BLOCK_R; r++)
    for(var c = 0; c < BLOCK_C; c++) {
      if(BLOCKS[currType][currDir][r][c]) {
        var div = $((currR + r) + '-' + (currC + c));
        div.style.backgroundColor = colors[currType];
        div.style.border = '1px solid ' + colors[currType];
      }
    }
}

function easeBlock() {
  for(var r = 0; r < BLOCK_R; r++)
    for(var c = 0; c < BLOCK_C; c++) {
      if(BLOCKS[currType][currDir][r][c]) {
        var div = $((currR + r) + '-' + (currC + c));
        div.style.backgroundColor = 'transparent';
        div.style.border = '1px solid ' + 'transparent';
      }
    }
}

function drawShadow() {
  for(var r = 0; r < BLOCK_R; r++)
    for(var c = 0; c < BLOCK_C; c++) {
      if(BLOCKS[currType][currDir][r][c]) {
        var div = $((shadow.r + r) + '-' + (shadow.c + c));
        div.style.border = '1px solid blue';
      }
    }
}

function easeShadow() {
  for(var r = 0; r < BLOCK_R; r++)
    for(var c = 0; c < BLOCK_C; c++) {
      if(BLOCKS[currType][currDir][r][c]) {
        var div = $((shadow.r + r) + '-' + (shadow.c + c));
        div.style.border = '1px solid ' + 'transparent';
      }
    }
}

function init() {
  var size = 28;

  var space = $('space');
  space.style.position = 'relative';
  space.style.width = size * MAP_C + (MAP_C + 1) * 3 + 1 + 'px';
  space.style.height = size * MAP_R + (MAP_R + 1) * 3 + 1 + 'px';
  //space.style.backgroundColor = 'lavender';
  space.style.border = '2px solid black';

  for(var r = 0; r < MAP_R; r++) {
    for(var c = 0; c < MAP_C; c++) {
      var div = document.createElement('div');
      div.id = r + '-' + c;
      div.style.position = 'absolute';
      div.style.top = size * r + (r + 1) * 3 + 'px';
      div.style.left = size * c + (c + 1) * 3 + 'px';
      div.style.width = size + 'px';
      div.style.height = size + 'px';
      space.appendChild(div);
    }
  }
}

function $(id) {
  return document.getElementById(id);
}
</script>
</html>


链接: https://fly63.com/article/detial/3365

如何使用SVG动画来制作游戏

在使用过一段时间的SVG动画之后,我相当清楚如何利用它来制作动画片段或者网页布局。一些动画库平台,例如Greensock,和原生的CSS动画简直是绝配。于是我便打算深入地研究一下,看看我能否用这些来制作一款简单的游戏

web游戏框架有哪些?_h5游戏开发引擎推荐

随着浏览器功能越来越强大,在网页端实现各种小游戏已经是家常便饭。微信小游戏出现后,游戏(特别是小型游戏)的开发已经进入了一个相当火爆的时期。那么作为一个普通的前端开发者,如何取选择一个合适的游戏开发框架来学习和提高开发效率呢?

three.js中物体旋转实践之房门的打开与关闭

看这篇博客,默认你已经知道了3D模型实现三维空间内旋转的实现方式(矩阵、欧拉角、四元数)。ok,下面正式切入主题,房门的打开和关闭,先上图:

js实现贪吃蛇小游戏

需要用到html、css、javascript 和 DOM 这些知识点就可以了。主要是js,其他只是一些基本的知识。js貌似也不是很难。但是问题就在这里,即使知识点都会了,但是还是无法综合运用把东西做出来

JS实现贪吃蛇

贪吃蛇游戏是一款经典的益智游戏,有PC和手机等多平台版本。既简单又耐玩。该游戏通过控制蛇头方向吃蛋,从而使得蛇变得越来越长。下面使用js来实现贪吃蛇的游戏

HTML5+Js实现拼图小游戏

当时初学游戏开发,经验浅薄,所以没有好好专研游戏里的算法和代码的缺陷,导致游戏出现了很多bug,甚至拼图打乱后很可能无法复原。最近经常有朋友问起这个游戏,希望我能把代码里的bug改一下方便初学者学习

html5游戏引擎有哪些?如何选择

如果您是游戏开发人员,并且正在寻找JavaScript和HTML5无缝协作的游戏引擎。 虽然市场上有很多免费和付费的游戏引擎,但一款让专业游戏开发者满意的游戏引擎,必须包含独特的东西

原生JavaScript实现贪吃蛇游戏

创建JavaScript贪吃蛇游戏需要使用 HTML 制作结构,使用 CSS 制作样式,使用 JavaScript 制作游戏逻辑。下面是一个简单的贪吃蛇游戏示例:

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!