HTML5 game development in practice|Tetris

 

Tetris is a video game console and handheld game console game that is popular all over the world. The sensation and economic value it caused can be said to be a major event in the history of games. This game seems simple but has endless changes. The game process only requires the player to move and flip the falling blocks of various shapes. If a row is full of blocks, then this row will be eliminated; The game is over when there are no more falling blocks.

It can be seen that the requirements of Tetris are as follows.

(1) Consists of moving blocks and fixed blocks that cannot move.

(2) Eliminate when a row is full.

(3) A variety of blocks can be generated.

(4) Players can see the points of the game.

This article develops the Tetris game program, and the running interface of the Tetris game is shown in the figure above.

01. Tetris game design ideas

Tetris shape design

The falling blocks in the game have various shapes. To draw blocks of different shapes in the game, it is necessary to use a reasonable data representation. The common Tetris has seven basic shapes and their deformed bodies after rotation. The specific shapes are shown in Figure 1.

■ Figure 1 Tetris shape

Each shape is composed of different small black squares. As shown in Figure 2, only necessary small black squares need to be displayed on the screen to display various shapes. Each shape is composed of 4 small squares. Composed of squares, it can be represented by 4 points.

■ Figure 2 schematic diagram of Tetris

What are the coordinates of the four points? Each shape has its own coordinate system. For example, an S-shape can be represented as shown in FIG. 3 .

■ Figure 3 S-shaped shape coordinate system

The S-shaped data model can be expressed as an array of 4 points: [[0,-1], [0,0], [-1,0], [-1,1]].

As shown in Figure 4, the T-shaped data model can be expressed as an array of 4 points: [[-1,0], [0,0], [1,0], [0,1]].

 

■ Figure 4 T-shape coordinate system

Array models of other shapes can be established in the same way, and then the array models of these 7 shapes can be combined to form a large array.

Additionally, each shape can be a single color or have its own color. Adding colors adds to the complexity of programming, but not by much, so colors are also considered in the model.

Finally, it's a good idea to give each shape a number so you can apply them in the shape and color arrays.

After completing the above analysis, the code of the shape data model can be given.

//各种形状的编号,0 代表没有形状NoShape = 0;
//z形//s形
ZShape = 1;
SShape = 2;
LineShape = 3;
//竖条形//T形
T斤爱败化仓臂氨尺暗隘肮伴唉懊碍艾蔼巴艾菠蔼懊胺报稗氨= 4;
//正方形//L形//反L形
SquareShape = 5 ;
LShape = 6;
MirroredLShape = 7
//各种形状的颜色
Colors =("black""fuchsia""# cff""red""orange","agua","green","yellow"];
[1,0 ],[ 0,0 ],[ 0,0 ],[ 0,0 ]
[0,-1],[ 0,0 ],[ 1,0 ],[ 1,1 ]
[0,-1],[ 0,0 ],[-1,0 ],[-1,1 ]
[0,-1],[ 0,0 ],[ 0,1 ],[ 0,21]
[-1,1],[ 0,1 ],[ 1,0 ],[ 0, 1]
[1, 1],[ 0,1 ],[ 0,0 ],[ 0,1 ]
[1,-1],[ 0,-1],[ 0,0],[ 0,1 ]

Tetris game panel screen

The panel of the game is composed of cells with a certain number of rows and columns. The panel screen of the game window is shown in Figure 5.

■ Figure 5 screen grid

The screen consists of a grid of 20 rows and 10 columns. In order to store the fixed squares in the game screen, a two-dimensional array lines is used. When the value of the corresponding array element is non-zero (the value of the array element is 0, indicating that there is no square in this cell), then Draw a corresponding small colored square. To display a Tetris shape in the window panel, you only need to draw the corresponding cells in the panel as colored squares. To display an L-shaped square in the panel as shown in Figure 6, you only need to define it according to the shape array of the L-shaped square. Use the Paint() function to draw its data to the window panel.

The basic processing method of the block drop is to move the current block down one line, and then redraw the screen according to the data of the current block array and the stored two-dimensional array lines of the fixed block panel, as shown in Figure 6. So use a coordinate (row, col) to record the row number row and column number col where the current square shape is located.

 

■ Figure 6 L-shaped cube before and after falling

Position and rotate shapes

1. position

As mentioned above, each shape is described in its own coordinate system, and there is also a global coordinate system on the screen to position the shape, so a method is needed to convert the 4 points of the shape from its own coordinate system to the global coordinate system on the screen to position the shape.

If the coordinates of the four points of the S shape in its own coordinate system are: [[0, -1], [0, 0], [-1, 0], [-1, 1]]. Its current position in the global coordinate system on the screen is: [12,8], then the coordinates of the four points transformed into the global coordinate system are: [[0+12, -1+8], [0+12, 0+8] ], [-1+12, 0+8], [-1+12, 1+8]]. In this way, the S-shaped global coordinate transformation is completed.

There is a problem to be noticed here. The coordinate system of the shape itself is described by (x, y), while the global coordinate system is described by (row, col) in order to be more logically intuitive, so it is not like the above in actual programming. Converted, instead: [[-1+12, 0+8], [0+12, 0+8], [0+12, -1+8], [1+12, -1+8]] That is, first change x to col, y to row, and then convert to the global coordinate system.

2. to rotate

The rotation is done around the origin of the shape in the shape's own coordinate system. The formula is simple. The relationship between the coordinates of each point after rotation and the coordinates before rotation is as follows (rotate to the right).

x'=y

y'=-x

Note that the square shape is not rotated.

According to the above analysis, two global methods can be used to globally position and rotate shapes.

The translate(data.row.col) function converts the coordinate system of the shape itself into the global coordinate system of the screen, and (row, col) is the position of the origin of the current shape on the screen.

function translate(data,row,col)(
var copy=[];
for(var i= 0;i< 4;i++){
   var temp ={
   ;temp.row = data[i][1] + row;temp.col = data[i][0]+ col;copy.push(temp);
return copy;

Each shape rotates to the right to form a new shape, and the rotate(data) function can get the coordinate array of the current shape after the rotation of the square.

//向右旋转形状:x'= y,y'= -xfunction rotate(data)!
var copy=[[],[],[][]];for(var i= 0;i<4;i++)
copy[i][0]= data[i][1];copyl[i][1]= -data[i][0];
return copy;

3. game flow

The Tetris game uses a timer to control the falling and redrawing process of the cube, and the user can use the keyboard input to change the state of the cube. Redraw the current falling block and the fixed block stored in lines at regular intervals, so as to see the dynamic game effect.

Various situations may be encountered during the falling of Tetris. For example, do you need to eliminate lines, do you need to stop falling and generate new shaped blocks, etc. The specific judgment process is as follows: first judge whether it can continue to fall, if it can fall, then row++ is enough; if the block cannot continue to fall, add the block of the current shape to the two-dimensional array lines of the panel, and the interface will generate a block of new shape. Then, judge whether to cancel the row. Finally, a redraw of the screen is requested.

 02. Tetris game design steps

 

Game page index.html

<html>
< head >
< title></title><meta http - equiv= "Content - Type" content = "text/html; charset = UTF - 8">< script type = "text/javascript" language = "javascript" src = "jsgame. js"> </script></head >
< body >
<audio src ="Kalimba.mp3” id = "snd">你的浏览器不支持 audio 标记
</audio >
<canvas id= "html5 09 1"width= "260"heght = "400"style= "background- color: Black">你的浏览器不支持 Canvas 标记,请使用 Chrome 览器或者 Eirefox 浏览器/canvas>
<canvas id= "html5 09 2"width= "100"height = "100" style = "background- color: red">你的浏览器不支持 Canvas 标记,请使用 Chrome 浏览器或者 Erefox 浏览器/canvas><p /
<div id="textmsg">分数</div>
<input type ="button"value ="开始”onclick ="start()”/><input type ="button”id="btnPause"value="暂停"onclick = "pause()"/>< script type ="text/javascript">

design script

1. Design block class Block

The type ID of the block is defined in the block class Block, and the two-dimensional array data and color of the shape of the block are stored. The translate(row, col) function is designed to convert the coordinate system of the shape itself into the coordinate system of the screen. The parameter (row, col) is the position of the origin of the current shape square in the screen Map. The rotate() function can get the coordinate array of the current shape block after rotation.

x
*方块类
*说明:各种形状的方块
function Block(
//this.data=[[],[],[],[]];
Block.prototype.Block = function ()this.born();
//产生一个新的形状方块Block.prototype.born = function () //随机选择一个形状this.shape id = Math.floor(Math.random() * 7)+1;this.data = Shapes[ this.shape id];this.color = Colors[ this.shape id];console.log(this.data);
//产生1~7的数
//存储方块部件的形状
//存储方块部件的颜色
//将形状自身的坐标系转换为屏幕 Map 的坐标系//(row,col)为当前形状的方块的原点在 Map 中的位置Block.prototype.translate = function (row,col)var copy=[l;
for(var i=0;i<4;i++) (
var temp =;
temp.row = this.datali][1] + row;
temp.col = this.datalill0]+ col;
copy.push(temp)
return copy;
//向右旋转一个形状:x'= y,y'= -x,得到旋转后的 dataBlock.prototype.rotate = function (){var copy=[[],[],[],[]];for(var i=0;i<4; i++) !copy[i][0]= this.datal i][1];copy[ i][1]=- this.datailol;
return copy;

In addition, in the program, the shapes of each block are numbered: number 1 for Z shape, number 2 for S shape, number 3 for vertical bar shape, number 4 for T shape, number 5 for square shape, number 6 for L shape, and number 7 for reverse L shape. The shape of all squares is stored in the array Shapes. Obtain the shape information of the block from the array Shapes by number.

//每一格的间距,即一个小方块的尺寸Spacing=20;
//各种形状的编号,0 代表没有形状
NoShape = 0;
ZShape = 1;
SShape = 2;
LineShape = 3;
IShape = 4;
SquareShape = 5;
LShape = 6;
MirroredLShape = 7
//各种形状的数据描述
Shapes =
[[O,0],[0,0],[0,0],[0,0]],[[O,-1],[0,0],[-1,0],[-1,1]],
[[o,-1],[0,0],[1,0],[1,1]],[[O,-1],[0,0],[0,1],[0,2]][[-1,0],[0,0],[1,0],[0,1]],[[O,0],[1,0],[0,1],[1,1]],[[-1,-1],[0,-1],[0,0],[0,1]],[[1,-1],[0,-1],[0,0],[0,1]]
//各种形状的颜色
Colors =["black""fuchsia","#cff","red","orange","aqua","green","yellow"l;

2. Design the game container Map class

The game container Map class is a game instance. The size of the game panel should be defined first, and the "container" of all squares - two-dimensional array lines is stored in the game panel. Initially, each element is stored as NoShape(0), indicating that there is no shape in this grid. box.

*
* Map类说明:由m行 Line 组成的格子阵*
function Map(w,h) [
//游戏区域的长度和宽度
this.width = w;
this.height = h;
//生成 height 个 line 对象,每个 line 宽度为 widththis.lines =];for(var row= 0; row < h; row++this.lines[row]= this.newLine():
//说明:由 n个格子组成的一行Map.prototype.newLine = function ()var shapes =;for(var col= 0; col < this.width; col++)shapes[ col]= NoShape;return shapes;

The isFullLine (row) function determines whether a row is fully occupied (full row), and returns False if there is a grid with NoShape.

Map.prototype.isFullLine = function (row) !var line = this.lines row];for(var col= 0; col < this.width; col++ )if(line[col]== NoShape)return false
return true;

Move or rotate the shape in advance, and call the isCollide(data) function to analyze whether the 4 points in the shape have the following collisions.

(1) col<0 || col>this.width, indicating that it exceeds the left and right boundaries.

(2) row==this. height, indicating that the shape has reached the bottom.

(3) If the shape_id of any point is not NoShape, a collision will occur.

If there is a collision, the movement or rotation is discarded.

Map.prototype.isCollide = function (data)for(var i=0;i<4;i++)fvar row = data il.row;var col = data[i].col;//console.log(row,col);if(col < 0col== this.width) return true;if(row == this.height) return true;
if(row <0) continue;
else
if(this.lines[row][col  != 0)//NoShapereturn true;
return false;

The shape collides during the downward movement, and appendShape=function (shape_id, data) adds the shape to the lines container and fixes it.

Map.prototype.appendShape = function( shape id,data)
//对于形状的 4个点
for(var i=0;i<4;i++)/
var row = datai].row;
var col = datalil.col;
//找到所在的格子,将格子的颜色改为形状的颜色
this.lines[row][col]= shape id;
//形状被加入 lines 容器中后,要进行逐行检测,发现满行则消除for(var row = 0; row < this.height; row++)
if(this.isFullLine(row)) 
//绘制消除效果
onClearRow(row);
/将满行删除
this.lines.splice(row,1);
//第一行添加新的一行
this.lines.unshift(this.newLine());

3. Design game logic class GameModel

The game logic class GameModel implements game control. First, it defines the game panel map, the current Tetris currentBlock, the next Tetris nextBlock, and the current Tetris location.

function GameModel(w,h)
this.map = new Map(w,h);
this.currentBlock = new Block();
this.currentBlock.Block();
this.row = 1;
this.col= Math.floor(this.map.width / 2);this.nextBlock = new Block();
//当前的俄罗斯方块
//当前的俄罗斯方块所在位置(顶端中央)
//下一个俄罗斯方块
this.nextBlock.Block();

Next, call the CreateNewBlock() function to generate a new Tetris block. It first copies the next shape this. nextBlock, generate the next Tetris.

GameModel.prototype.CreateNewBlock = function()
this.currentBlock = this.nextBlock;
this.row = 1;
this.col = Math.floor(this.map.width / 2);
//复制预览区形状
//重置形状的位置为出生地点(顶端中央)
this.nextBlock = new Block();
this.nextBlock.Block();

The following is to control the left and right movement, rotation and down movement of the shape block, and ensure that it does not collide with the fixed block and boundary stored in lines when moving left and right. If there is a collision, the data is recovered and the movement is abandoned.

//向左移动
GameModel.prototype.left = function()
this.col-- ;var temp = this.currentBlock.translate(this.row,this.col);if(this.map.isCollide(temp))
//发生碰撞则放弃移动
this.col++;
//通知数据发生了更新
else
onUpdate();
//向右移动
GameModel.prototype.right = function()this.col++;
var temp= this.currentBlock.translate(this.row,this.col);if(this.map.isCollide(temp))this.col-- ;
else
onUpdate();

Also ensure that the rotation does not collide with the fixed squares and boundaries stored in lines. If it collides, the data is restored and the rotation is discarded.

//旋转
GameModel.prototype.rotate = function()
//正方形不旋转
if(this.currentBlock.shape id== SquareShape) return;//获得旋转后的数据
var copy = this.currentBlock.rotate();
//转换坐标系
var temp = this.currentBlock.translate(this.row, this.col);//发生碰撞则放弃旋转
if(this.map.isCollide(temp))
return;
//将旋转后的数据设为当前数据this.currentBlock.data = copy;//通知数据发生了更新

The falling of the cube needs to judge whether it has "bottomed out" or touched other fallen cubes. If it "touches the bottom", it is fixed to the game panel. At this time, it is necessary to handle the judgment of full line and game end, and generate new Tetris at the same time.

//下落
GameModel.prototype.down = function()
var old= this.currentBlock.translate(this.row,this.col);this.row++ ;
var temp= this.currentBlock.translate(this.row,this.col);if(this.map.isCollide(temp)) //发生碰撞则放弃下落this.row-- ;//如果在 1 也无法下落,则说明游戏结束if(this.row== 1)(
//通知游戏结束
onGameOver();
return;
//无法下落则将当前形状加入 Map 中this.map.appendShape(this.currentBlock.shape id,old);this.CreateNewBlock();
//产生新的俄罗斯方块
//通知数据发生了更新
onUpdate();

4. game main program

In the timed event, complete the drop function.

var display = document.getElementById("htm15 09 1");var display2 = document.getElementById("htm15_09 2");
var model = null;
//游戏面板//预览区域
var loop interval= null;
var tick interval = null:
var waiting = false;
var speed = 500;
v珠跋糊傲安把奥樊喽唉哎啊爱碍暗跋拔敖稗傲八core = 0;
var textmsg = document.getElementById("textmsg”);function start)!
model = new GameModel(display.width / Spacing,display.height / Spacing);loop(;
function pause()waiting =!waiting;if(waiting)document.getElementById("btnPause").value ="继续”;else
document.getElementById("btnPause").value ="暂停”;
//消息循环function loop(){tick interval= setInterval(function()if(waiting) return;onTick() ;
//时钟事件即下落

The following is the message event handling.

//消息处理//更新事件
function onUpdate() {
   paint();
//清除行事件
function onClearRow(row) !clearline(row);
score = score +10:
textmsq.innerHTML = score +“分”
/游戏结束事件function onGameOver()alert("Game Over");clearInterval(tick interval);
//时钟事件function onTick()model.down();
//按键事件
function onKevPress(evt) evt.preventDefault():move(evt.which);
function move(which) !

switch(which)
case 37: model.left(); break;
case 39: model.right(); break;
case 38: model.rotate(); break;
case 40: model.down(); break;

The following is the real drawing code, calling the clearline(row) function to draw the pause effect of clearing the line.

function clearline(row)
//增加速度
clearInterval(tick interval);
speed = speed -10;
loop();
//音效
document.getElementById("snd").play()//停顿效果
waiting = true;
var ctx = display.getContext("2d");
ctx.fillRect(0,row * Spacing,display.width,Spacing,"black");setTimeout("waiting = false;",50);

Call the paint() function to draw the game screen. It draws all fixed blocks stored in lines to the game panel, and at the same time draws the current block to the game panel and the next block to the prompt preview area on the right side of the game panel.

function paint()
var map = model.map;
var data = model.currentBlock.translate(model.row,model.col);var nextdata = model.nextBlock.translate(1,2);//在预览区(1,2)处位置//清屏
var ctx = display.getContext("2d");
ctx.clearRect(0,0,display.width,display.height);
var ctx2 = display2.getContext("2d");ctx2.clearRect(0,0,display2.width,display2.height);
var lines = map.lines;
//游戏面板中依次绘制每一个非空的格子(固定的方块)for(var row= 0; row < map.height; row++)
for(var col=0; col < map.width; col++)[
var shape id= lines[ row] coll;if(shape id != NoShape) {
   var y=row * Spacing;var x=col * Spacing;
var color = Colors[ shape id];
var ctx = display.getContext("2d");
ctx.fillStyle ="rgba(255,255,255,0.2)";
ctx.fillRect(x,y,Spacing,Spacing);
//形状前景色
ctx.fillStyle= color;
ctx.fillRect(x+1,y+1,Spacing - 2,Spacing - 2);
//绘制当前的方块  

for(var i=0;i<4;i++)
var y=datalil.row * Spacing;
var x= data[i].col * Spacing;
var color = model.currentBlock.color:
var ctx = display.getContext("2d");
ctx.fillStyle="rgba(255,255,255,0.2)";
/Colors[model. currentBlock. shape id];
ctx.fillRect(x,y,Spacing,Spacing);
//形状前景色ctx.fillRect(x+1,y+1,Spacing -2,Spacing - 2);
ctx.fillStyle = color;
//绘制预览区中下个方块
for(var i=0;i<4;i++) !
var y= nextdatali].row * Spacing;
var x= nextdatali].col * Spacing;
/display2.draw( Rects[model.nextshape id],x,y);//Colors[model.nextBlock.shape id];
var color = model.nextBlock.color;
var ctx2 = display2.getContext("2d");
ctx2.fillStyle="rqba(255,255,255,0.2)"
ctx2.fillRect(x,y,Spacing,Spacing);
ctx2.fillStyle= color;
//形状前景色
ctx2.fillRect(x+1,y+1,Spacing - 2,Spacing - 2);

At this point, the Tetris game is written.

 

Guess you like

Origin blog.csdn.net/qq_41640218/article/details/131502467