javascript 俄罗斯方块 游戏

0、前言


  最近在玩js,感知js确是一个强大的脚本语言,加之html5这样一个准标准的推出,相信js在未来的web app中发挥的作用会越来越大。过去几年时间都在做后台方面的一些开发,很少会触及到web方面的开发,作为一名软件工程师,多补充一点新的知识,为自己充充电还是很有必要,且有趣的。


一、游戏设计思想


  是一款风靡全球的电视游戏机和掌上游戏机游戏,它由俄罗斯人阿列克谢·帕基特诺夫发明,故得此名。俄罗斯方块的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。由于上手简单、老少皆宜,从而家喻户晓,风靡世界。

  在网页中设计这样一款游戏:

  1.在界面方面,我们采用html标签“table”作用这款游戏的整体布局,“table”的子标签“td”为最基本的方块单元。

  2.通过改变方块单元的颜色来表示每个方块形状的移动过程。

  3.接收并处理DOM模型中的document的onkeydown事件,从而左、右、下移方块形状。


二、实现过程及用到的技巧


  首先是设计游戏地图的数据结构。

  游戏主界面如下图所示:

  javascript 俄罗斯方块 游戏_第1张图片

  地图的数据结构为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从入门到精通》

你可能感兴趣的:(JavaScript,html,数据结构,游戏,function,colors)