Preface
Using es6 to write small games such as Tetris has become an old thing.
But it is necessary for us as beginners to practice.
The main reason is that I wrote one by chance, and there are no other deeper factors.
...
This project is written with es6 and object-oriented.
CELL model
- Save the coordinates and class of each cell;
- Drawing div
- Eliminate div
In this way, the advantage of object-oriented is that it will be very convenient to write in the future, so there is no need to consider too many other methods.
box model
constructor(map, fixed, arr, color) { this.blockCells = []; this.color = color; this.arr = arr; //The shape of the square this.fixed = fixed; //Whether the position is random this.x = 1; //Record movement position this.y = 1; //Record movement position this.map = map; this.pos = null; //Location of all block cell s [] this.rotateFlag = true; //Can I rotate this.moveFlag = true; //Whether it can be moved }
Initialization
- Randomly generate block shape
- Randomly generate block coordinates
- Randomly generate block color
- Render out blocks
Square shape
Use this.arr to save the shape of the current block;
Use two-dimensional array to save various shapes
[ [1, 0], [1, 0], [1, 1] ]
This one represents a 2 * 3 L-shaped square, 1 represents the position to be rendered, 0 represents the position not to be rendered;
Draw blocks
- First, we analyze the corresponding 1 and 0 in the shape 2D array.
- Draw from 1 and 0
Get the coordinates of 1
getXY()
Double loop the shape to get the position corner of 1 [0,0], [1,0], [2,0], [2,1], and then render according to the position corner.
Variation of Spin
In fact, you can use arrays to write out all shapes and corresponding rotation shapes, and then call them in the array. But when I was young, I wrote a rotation formula.
If you rotate clockwise, suppose
[ [1, 0], [1, 0], [1, 1] ]
Transform to
[ [1, 1, 1], [1, 0, 0] ]
It can be found that all coordinates of 1 and 0 are only x and y interchanged (the same 1 or 0 does not have corresponding calling relationship);
So it's actually a question of the transposition of the approximation matrix, but there are still differences.
let rotateArr = []; for (let i = 0, len = arr.length; i < len; i++) { let inArr = []; for (let j = arr.length - 1; j >= 0; j--) { inArr.push(arr[j][i]); } rotateArr.push(inArr); } return rotateArr;
Redraw after rotation
In fact, there is no need to redraw the dom after rotation, so as to save dom operation.
Add a decision in getXY(). After obtaining the coordinates of 1, whether to initialize the drawing block or to rotate the moving block after completion;
At the end of each step, you need to record the position in the block, otherwise the rotation will return to the starting point after the end of the change
Judgement boundary
This is to detect according to the coordinate position occupied by the next step, which is needed for moving and rotating.
-
move
- Get the position of the next step in the direction of movement
- Judgement boundary
-
rotate
- Obtain the position of the new shape formed after rotation
- Judgement boundary
Judgement logic
-
Block accumulation
- Use an array to store all stacked grid positions to see if they coincide with the next step
-
No block accumulation
- Determine whether the next position exceeds the boundary of map.
move
Because the cell class has been written before, the move here is actually very simple. If the boundary judgment is passed, just move each square in the square to the next position obtained.
- Coordinate movement of block object
- dom operation corresponds to block object movement
Pile up
This is to record the stacked blocks when they reach the boundary downward, and change the class.
// Record deadblock location information to map this.blockCells.forEach(el => { for (let i = 0, len = map.deadBlocks.length; i < len; i++) { if (el.x === map.deadBlocks[i][0] && el.y === map.deadBlocks[i][1]) break; map.deadBlocks.push(el); break; } if (!map.deadBlocks.length) map.deadBlocks.push(el); }) // Change class this.blockCells.forEach(el => { el.cell.style.background = '#7f8c8d'; }) //Elimination this.eliminate(); //Control game restart game.stop(); game.start();
Elimination
Traverse the stacked blocks of all records. When the number of blocks in the same row is the same as the map size, delete the corresponding blocks in the array and dom.
for (let r = MAP_COL - 1; r > 0; r--) { let idxs = [] let num = 0; //Number of cell s in this line deadBlocks.forEach((el, i) => { if (el.y === 0) { game.over(); return; } if (el.y === r) { num++; idxs.push(i); }; }) // Elimination if (num === MAP_LEN) { idxs.forEach(i => { deadBlocks[i].remove(this.map); delete deadBlocks[i]; }) //Move down deadBlocks.forEach((el, i) => { if (el.y < r) el.move(el.x, el.y + 1); }) r++; //Adjust the current number of rows //Score game.addScore(); } }
Game model
Add a timer to the game to realize the automatic drop of the box; remove the timer when the game is stopped;
- start-up
- Score
-
suspend
handlePause() { this.pause = !this.pause; if (this.pause) { this.stop(); return; } this.timer = setInterval(() => { block.check('down') }, this.speed); }
- Game over
Summary
- There is no variable to adjust the difficulty of the game.
- No direct drop function
Portal
Welcome to star if you like