Home  >  Article  >  Web Front-end  >  JavaScript implementation of Tetris game process analysis and source code sharing_javascript skills

JavaScript implementation of Tetris game process analysis and source code sharing_javascript skills

WBOY
WBOYOriginal
2016-05-16 16:08:141277browse

Watch "The Beauty of Programming": "Although programs are difficult to write, they are beautiful. To write programs well, you need to have certain basic knowledge, including programming languages, data structures and algorithms. If you write programs well, Requires rigorous logical thinking skills and a good foundation of analysis, as well as familiarity with programming environments and programming tools. ”

After studying computers for several years, have you ever fallen in love with programming? In other words, if you haven’t tried to write a game yourself, you don’t really love programming.

The sensation and economic value Tetris once caused can be said to be a major event in the history of games. It seems simple but is endlessly varied and addictive. I believe that most of my classmates were once so obsessed with it that they stopped thinking about food and tea.

Game Rules

1. A flat virtual venue for placing small squares. Its standard size is: row width is 10, column height is 20, with each small square as the unit.

2. A set of regular graphics composed of 4 small squares. It is called Tetromino in English and commonly known as square in Chinese. There are 7 types, each with the letters S, Z, L, J, I, O, and T. Name the shape.

I: Eliminate up to four layers at a time

J (left and right): Eliminate up to three layers, or eliminate two layers

L: Eliminate up to three layers, or eliminate two layers

O: Eliminate the first to second floors

S (left and right): up to two layers, easy to cause holes

Z (left and right): up to two layers, easy to cause holes

T: Up to two floors

The blocks will slowly continue to fall from above the area. Players can rotate the block in units of 90 degrees and move the block left and right in units of grids to accelerate the block to fall. When a block moves to the bottom of the area or lands on other blocks and cannot move, it will be fixed there, and new blocks will appear above the area and begin to fall. When a column of horizontal grids in the area is completely filled with squares, the column will disappear and become the player's score. The more columns are deleted at the same time, the score index increases.

Analysis and solution

When each block falls, we can do:

1) Rotate to the appropriate direction

2) Move horizontally to a certain column

3) Drop vertically to the bottom

First, you need to use a two-dimensional array, area[18][10] represents the 18*10 game area. Among them, a value of 0 in the array means empty, and a value of 1 means there are blocks.

There are 7 types of blocks in total, each with 4 directions. Define activeBlock[4]. The value of this array is precalculated before compilation and used directly in the program.

Difficulties

1) Boundary check.

 //检查左边界,尝试着朝左边移动一个,看是否合法。
function checkLeftBorder(){ 
  for(var i=0; i<activeBlock.length; i++){ 
    if(activeBlock[i].y==0){ 
      return false; 
    } 
    if(!isCellValid(activeBlock[i].x, activeBlock[i].y-1)){ 
      return false; 
    } 
  } 
  return true; 
} //同理,需要检测右边界和底边界

2) Rotation requires mathematical logic and is a problem of rotating one point 90 degrees relative to another point.
3) Timing and monitoring keyboard event mechanisms allow the game to run automatically.

//开始 
function begin(e){ 
  e.disabled = true; 
  status = 1; 
  tbl = document.getElementById("area"); 
  if(!generateBlock()){ 
    alert("Game over!"); 
    status = 2; 
    return; 
  } 
  paint(); 
  timer = setInterval(moveDown,1000); 
} 
document.onkeydown=keyControl;

Procedure process

1) The user clicks Start->Construct an active graphic and set the timer.

//当前活动的方块, 它可以左右下移动, 变型。当它触底后, 将会更新area; 
var activeBlock; 
//生产方块形状, 有7种基本形状。 
function generateBlock(){ 
  activeBlock = null; 
  activeBlock = new Array(4); 
  //随机产生0-6数组,代表7种形态。
  var t = (Math.floor(Math.random()*20)+1)%7; 
  switch(t){ 
    case 0:{ 
      activeBlock[0] = {x:0, y:4}; 
      activeBlock[1] = {x:1, y:4}; 
      activeBlock[2] = {x:0, y:5}; 
      activeBlock[3] = {x:1, y:5}; 

      break; 
    } 
    //省略部分代码..............................
    case 6:{ 
      activeBlock[0] = {x:0, y:5}; 
      activeBlock[1] = {x:1, y:4}; 
      activeBlock[2] = {x:1, y:5}; 
      activeBlock[3] = {x:1, y:6}; 
      break; 
    } 
  } 
  //检查刚生产的四个小方格是否可以放在初始化的位置. 
  for(var i=0; i<4; i++){ 
    if(!isCellValid(activeBlock[i].x, activeBlock[i].y)){ 
        return false; 
      } 
    } 
  return true; 
}

2) After each downward movement, check whether it has hit the bottom. If it has hit the bottom, try to eliminate the row.

//消行 
function deleteLine(){ 
  var lines = 0; 
  for(var i=0; i<18; i++){ 
    var j=0; 
    for(; j<10; j++){ 
      if(area[i][j]==0){ 
        break; 
    } 
  } 
  if(j==10){ 
    lines++; 
    if(i!=0){ 
      for(var k=i-1; k>=0; k--){ 
        area[k+1] = area[k]; 
      } 
    } 
    area[0] = generateBlankLine(); 
    } 
  } 
  return lines; 
}

3) After finishing, construct an active graphic and set the timer.


Rendering

To be optimized

1) Set the colors of blocks of different shapes.

Idea: In the create block function, set the activeBlockColor color. The seven different shapes of blocks have different colors (in addition to modifying the generateBlock method, you also need to modify the paintarea method. Because it was not well thought out at the beginning, after eliminating a row, repeat Draw the squares while unifying the colors, so you can consider removing n rows from the table, and then adding n rows at the top to ensure the integrity of the squares without eliminating them).

2) When the current block falls, you can check the next block in advance.

Idea: Split the generateBlock method into two parts, one part is used to randomly try the next block, and the other part is used to cache the current block to be drawn. When the current block hits the bottom and is fixed, the next block begins to be drawn, and new blocks are randomly generated again. Repeatedly.

Complete HTML source code:

<!DOCTYPE>
<html> 
<head> 
<title>Tetris</title> 
<meta charset="UTF-8">
<style> 
*{
	font-family: "微软雅黑";
}
.tetrisContainer{
	width: 230px;
	height: 400px;
	position: relative;
	left: 50%;
	margin-left: -115px;
	top: 40%;
	margin-top: -200px;
}
#area tr td{ 
	width: 20px; 
	height: 20px; 
	border:1px solid #ccc;
} 
</style> 
</head> 
	<body> 
		<div class = "tetrisContainer">
			<input type="button" value="开始游戏" onclick="begin(this);"/> 得分: <span id="score"> 0</span>
			<table id="area" cellspacing="0" cellpadding="0" border="1" style="border-collapse:collapse"><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr></table>
		</div>
	</body> 
	<script type="text/javascript" src="script/tetris.js"></script>
</html> 

Complete tetris.js source code:

/** 
* JS俄罗斯方块游戏 v 1.0
*/ 
//表示页面中的table, 这个table就是将要显示游戏的主面板 
var tbl; 
//游戏状态 0: 未开始;1 运行; 2 中止; 
var status = 0; 
//定时器, 定时器内将做moveDown操作 
var timer; 
//分数 
var score = 0; 

//area是一个18*10的数组,也和页面的table对应。初始时都为0, 如果被占据则为1 

var area = new Array(18); 
for(var i=0;i<18;i++){ 
	area[i] = new Array(10); 
} 
for(var i=0;i<18;i++){ 
	for(var j=0; j<10; j++){ 
		area[i][j] = 0; 
	} 
} 

//当前活动的方块, 它可以左右下移动, 变型。当它触底后, 将会更新area; 
var activeBlock; 
//生产方块形状, 有7种基本形状。 
function generateBlock(){ 
	activeBlock = null; 
	activeBlock = new Array(4); 
	//随机产生0-6数组,代表7种形态。
	var t = (Math.floor(Math.random()*20)+1)%7; 
	switch(t){ 
		case 0:{ 
			activeBlock[0] = {x:0, y:4}; 
			activeBlock[1] = {x:1, y:4}; 
			activeBlock[2] = {x:0, y:5}; 
			activeBlock[3] = {x:1, y:5}; 

			break; 
		} 
		case 1:{ 
			activeBlock[0] = {x:0, y:3}; 
			activeBlock[1] = {x:0, y:4}; 
			activeBlock[2] = {x:0, y:5}; 
			activeBlock[3] = {x:0, y:6}; 
			break; 
		} 
		case 2:{ 
			activeBlock[0] = {x:0, y:5}; 
			activeBlock[1] = {x:1, y:4}; 
			activeBlock[2] = {x:1, y:5}; 
			activeBlock[3] = {x:2, y:4}; 
			break; 
		} 
		case 3:{ 
			activeBlock[0] = {x:0, y:4}; 
			activeBlock[1] = {x:1, y:4}; 
			activeBlock[2] = {x:1, y:5}; 
			activeBlock[3] = {x:2, y:5}; 
			break; 
		} 
		case 4:{ 
			activeBlock[0] = {x:0, y:4}; 
			activeBlock[1] = {x:1, y:4}; 
			activeBlock[2] = {x:1, y:5}; 
			activeBlock[3] = {x:1, y:6}; 
			break; 
		} 
		case 5:{ 
			activeBlock[0] = {x:0, y:4}; 
			activeBlock[1] = {x:1, y:4}; 
			activeBlock[2] = {x:2, y:4}; 
			activeBlock[3] = {x:2, y:5}; 
			break; 
		} 
		case 6:{ 
			activeBlock[0] = {x:0, y:5}; 
			activeBlock[1] = {x:1, y:4}; 
			activeBlock[2] = {x:1, y:5}; 
			activeBlock[3] = {x:1, y:6}; 
			break; 
		} 
	} 
	//检查刚生产的四个小方格是否可以放在初始化的位置. 
	for(var i=0; i<4; i++){ 
		if(!isCellValid(activeBlock[i].x, activeBlock[i].y)){ 
				return false; 
			} 
		} 
	return true; 
} 
//向下移动 
function moveDown(){ 
	//检查底边界. 
	if(checkBottomBorder()){ 
		//没有触底, 则擦除当前图形, 
		erase(); 
		//更新当前图形坐标 
		for(var i=0; i<4; i++){ 
			activeBlock[i].x = activeBlock[i].x + 1; 
		} 
		//重画当前图形 
		paint(); 
	} 
	//触底, 
	else{ 
		//停止当前的定时器, 也就是停止自动向下移动. 
		clearInterval(timer); 
		//更新area数组. 
		updatearea(); 
		//消行 
		var lines = deleteLine(); 
		//如果有消行, 则 
		if(lines!=0){ 
			//更新分数 
			score = score + lines*10; 
			updateScore(); 
			//擦除整个面板 
			erasearea(); 
			//重绘面板 
			paintarea(); 
		} 
		//产生一个新图形并判断是否可以放在最初的位置. 
		if(!generateBlock()){ 
			alert("Game over!"); 
			status = 2; 
			return; 
		} 
		paint(); 
		//定时器, 每隔一秒执行一次moveDown 
		timer = setInterval(moveDown,1000) 
	} 
} 
//左移动 
function moveLeft(){ 
	if(checkLeftBorder()){ 
		erase(); 
		for(var i=0; i<4; i++){ 
			activeBlock[i].y = activeBlock[i].y - 1; 
		} 
		paint(); 
		} 
	} 
	//右移动 
function moveRight(){ 
	if(checkRightBorder()){ 
		erase(); 
		for(var i=0; i<4; i++){ 
			activeBlock[i].y = activeBlock[i].y + 1; 
		} 
		paint(); 
	} 
} 
//旋转, 因为旋转之后可能会有方格覆盖已有的方格. 
//先用一个tmpBlock,把activeBlock的内容都拷贝到tmpBlock, 
//对tmpBlock尝试旋转, 如果旋转后检测发现没有方格产生冲突,则 
//把旋转后的tmpBlock的值给activeBlock. 
function rotate(){ 
	var tmpBlock = new Array(4); 
	for(var i=0; i<4; i++){ 
		tmpBlock[i] = {x:0, y:0}; 
	} 
	for(var i=0; i<4; i++){ 
		tmpBlock[i].x = activeBlock[i].x; 
		tmpBlock[i].y = activeBlock[i].y; 
	} 
	//先算四个点的中心点,则这四个点围绕中心旋转90度。 
	var cx = Math.round((tmpBlock[0].x + tmpBlock[1].x + tmpBlock[2].x + tmpBlock[3].x)/4); 
	var cy = Math.round((tmpBlock[0].y + tmpBlock[1].y + tmpBlock[2].y + tmpBlock[3].y)/4); 
	//旋转的主要算法. 可以这样分解来理解。 
	//先假设围绕源点旋转。然后再加上中心点的坐标。 

	for(var i=0; i<4; i++){ 
		tmpBlock[i].x = cx+cy-activeBlock[i].y; 
		tmpBlock[i].y = cy-cx+activeBlock[i].x; 
	} 
	//检查旋转后方格是否合法. 
	for(var i=0; i<4; i++){ 
	if(!isCellValid(tmpBlock[i].x,tmpBlock[i].y)){ 
		return; 
	} 
} 
//如果合法, 擦除 
erase(); 
//对activeBlock重新赋值. 
for(var i=0; i<4; i++){ 
	activeBlock[i].x = tmpBlock[i].x; 
	activeBlock[i].y = tmpBlock[i].y; 
} 
//重画. 
paint(); 
} 
//检查左边界,尝试着朝左边移动一个,看是否合法。
function checkLeftBorder(){ 
	for(var i=0; i<activeBlock.length; i++){ 
		if(activeBlock[i].y==0){ 
			return false; 
		} 
		if(!isCellValid(activeBlock[i].x, activeBlock[i].y-1)){ 
			return false; 
		} 
	} 
	return true; 
} 
//检查右边界,尝试着朝右边移动一个,看是否合法。
function checkRightBorder(){ 
	for(var i=0; i<activeBlock.length; i++){ 
		if(activeBlock[i].y==9){ 
			return false; 
		} 
		if(!isCellValid(activeBlock[i].x, activeBlock[i].y+1)){ 
			return false; 
		} 
	} 
	return true; 
} 
//检查底边界,尝试着朝下边移动一个,看是否合法。
function checkBottomBorder(){ 
	for(var i=0; i<activeBlock.length; i++){ 
		if(activeBlock[i].x==17){ 
			return false; 
		} 
		if(!isCellValid(activeBlock[i].x+1, activeBlock[i].y)){ 
			return false; 
		} 
	} 
	return true; 
} 
//检查坐标为(x,y)的是否在area种已经存在, 存在说明这个方格不合法。
function isCellValid(x, y){ 
	if(x>17||x<0||y>9||y<0){ 
		return false; 
	} 
	if(area[x][y]==1){ 
		return false; 
	} 
	return true; 
} 
//擦除 
function erase(){ 
	for(var i=0; i<4; i++){ 
		tbl.rows[activeBlock[i].x].cells[activeBlock[i].y].style.backgroundColor="white"; 
	} 
} 
//绘活动图形 
function paint(){ 
	for(var i=0; i<4; i++){ 
		tbl.rows[activeBlock[i].x].cells[activeBlock[i].y].style.backgroundColor="#CC3333"; 
	} 
} 
//更新area数组 
function updatearea(){ 
	for(var i=0; i<4; i++){ 
		area[activeBlock[i].x][activeBlock[i].y]=1; 
	} 
} 
//消行 
function deleteLine(){ 
	var lines = 0; 
	for(var i=0; i<18; i++){ 
		var j=0; 
		for(; j<10; j++){ 
			if(area[i][j]==0){ 
				break; 
		} 
	} 
	if(j==10){ 
		lines++; 
		if(i!=0){ 
			for(var k=i-1; k>=0; k--){ 
				area[k+1] = area[k]; 
			} 
		} 
		area[0] = generateBlankLine(); 
		} 
	} 
	return lines; 
} 
//擦除整个面板 
function erasearea(){ 
	for(var i=0; i<18; i++){ 
		for(var j=0; j<10; j++){ 
			tbl.rows[i].cells[j].style.backgroundColor = "white"; 
		} 
	} 
} 
//重绘整个面板 
function paintarea(){ 
	for(var i=0;i<18;i++){ 
		for(var j=0; j<10; j++){ 
			if(area[i][j]==1){ 
				tbl.rows[i].cells[j].style.backgroundColor = "#CC3333"; 
			} 
		} 
	} 
} 
//产生一个空白行. 
function generateBlankLine(){ 
	var line = new Array(10); 
	for(var i=0; i<10; i++){ 
		line[i] = 0; 
	} 
	return line; 
} 
//更新分数 
function updateScore(){ 
	document.getElementById("score").innerText=" " + score; 
} 
//键盘控制 
function keyControl(){ 
	if(status!=1){ 
		return; 
	} 
	var code = event.keyCode; 
	switch(code){ 
		case 37:{ 
			moveLeft(); 
			break; 
		} 
		case 38:{ 
			rotate(); 
			break; 
		} 
		case 39:{ 
			moveRight(); 
			break; 
		} 
		case 40:{ 
			moveDown(); 
			break; 
		} 
	} 
} 
//开始 
function begin(e){ 
	e.disabled = true; 
	status = 1; 
	tbl = document.getElementById("area"); 
	if(!generateBlock()){ 
		alert("Game over!"); 
		status = 2; 
		return; 
	} 
	paint(); 
	timer = setInterval(moveDown,1000); 
} 
document.onkeydown=keyControl; 
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn