The first time I wrote Tetris was more than a year ago, and I had just learned js not long ago.
In order to strengthen my understanding of JS and add my love for games, I wrote Tetris based on canvas using the most basic JS code without referring to other people's ideas and codes.
During the summer vacation of my junior year, I used the syntax of es6 to improve it, including syntactic sugar of class, arrow functions, etc., to further increase my understanding of es6. The code has 400+ lines.
If you want to make this little game, you must first be familiar with H5 canvas, js array processing, keyboard event monitoring and processing, timer use, etc. The rest is basic logic processing.
The rules of the game are the core and the top priority of our code
The core logic here is to determine whether the block collides (the currently moving block collides with the already positioned block so that the currently moving block cannot move downward, because our block moves downward by default. If it cannot move downward, Walking down is considered as a block that has been positioned, and then a new block is generated and continues to move down from the initial position).
And this collision also needs to be applied when the block is deformed. Similarly, if the block collides with other positioned blocks during the deformation process, we should prevent the block from deforming successfully.
The code is attached, discussions and corrections are welcome.
<!DOCTYPE html><html lang=en><head> <meta charset=UTF-8> <title>es6-Reconstruction of Tetris (based on canvas)</title> <style type=text/css> #tetris{ margin: 10px 250px;} </style></head><body> <canvas width=700 height=525 id=tetris></canvas> <div id=text style='color: red;font-size: 30px;'>Current score: 0</div> <script type=text/javascript> /** * [A complete Tetris class design by magic_xiang] * @param {number} side [length of each square side (px), default 35] * @param {number} width [number of squares in one row (number), default 20] * @param {number} height [Number of blocks included in a column (default 15)] * @param {number} speed [Block falling movement speed (ms), default 400] */ class tetris{ constructor(side=35, width=20, height=15 , speed=400){ this.side = side // The side length of each block this.width = width // The number of blocks contained in a row this.height = height // The number of blocks contained in a column this.speed = speed // The falling movement speed of the block this.num_blcok // The numeric variable of the current block type this.type_color // The string variable of the current color type this.ident // The identifier of setInterval this.direction = 1 // The direction of the block, initialized to 1, default Status this.grade = 0 // Used to calculate scores this.over = false // Whether the game is over this.arr_bX = [] // Store the X coordinate of the current block this.arr_bY = [] // Store the Y coordinate of the current block this.arr_store_X = [] // Store the X coordinate of all blocks that have reached the bottom this.arr_store_Y = [] // Store the Y coordinate of all blocks that have reached the bottom this.arr_store_color = [] // Store all blocks that have reached the bottom The color of the square this.paints = document.getElementById('tetris').getContext('2d') //Get the brush self = this } // Encapsulate the paints method to make the code more concise paintfr(x, y, scale=1){ this.paints.fillRect(x*this.side, y*this.side, scale*this.side, scale*this.side) } // The game starts gameStart(){ this.init() this.run() } // Initialization work init(){ this.initBackground() this.initBlock() } // The block automatically falls run(){ this.ident = setInterval(self.down_speed_up(), this.speed) } // Initialize the map initBackground(){ this.paints.beginPath() this.paints.fillStyle='#000000' / /The map fill color is black for(let i = 0; i < this.height; i++){ for(let j = 0; j < this.width; j++){ this.paintfr(j, i) } } this.paints.closePath() } // Initialize the position and color of the block initBlock(){ this.paints.beginPath() this.createRandom('rColor') // Generate a color string, this.paints.fillStyle = this.type_color this.createRandom('rBlock') //Generate block type numbers this.arr_bX.forEach((item, index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // Use array to draw Block drawBlock(color){ this.paints.beginPath() this.paints.fillStyle = color this.arr_bX.forEach((item, index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // Draw the already positioned block drawStaticBlock( ){ this.arr_store_X.forEach((item, index) => { this.paints.beginPath() this.paints.fillStyle = this.arr_store_color[index] this.paintfr(item, this.arr_store_Y[index], 0.9) this.paints.closePath() }) } // Generate a random number and return the block type or color type createRandom( type){ let temp = this.width/2-1 if (type == 'rBlock'){ //If it is a block type this.num_blcok = Math.round(Math.random()*4+1) switch(this.num_blcok){ case 1: this.arr_bX.push(temp,temp-1,temp,temp+ 1) this.arr_bY.push(0,1,1,1) break case 2: this.arr_bX.push(temp,temp-1,temp-1,temp+1) this.arr_bY.push(1,0,1,1) break case 3: this.arr_bX.push(temp,temp-1,temp+1,temp+2) this.arr_bY.push(0,0,0, 0) break case 4: this.arr_bX.push(temp,temp-1,temp,temp+1) this.arr_bY.push(0,0,1,1) break case 5: this.arr_bX.push(temp,temp+1,temp,temp+1) this.arr_bY.push(0,0,1,1) break } } if (type == 'rColor'){ //if It is the color type let num_color = Math.round(Math.random()*8+1) switch(num_color){ case 1: this.type_color='#3EF72A' break case 2: this.type_color='yellow' break case 3: this.type_color='#2FE0BF' break case 4: this.type_color='red' break case 5: this.type_color='gray' break case 6: this.type_color ='#C932C6' break case 7: this.type_color= '#FC751B' break case 8: this.type_color= '#6E6EDD' break case 9: this.type_color= '#F4E9E1' break } } } // Determine whether the blocks collide (lower), and whether they cross the lower boundary during deformation judgeCollision_down(){ for(let i = 0; i < this.arr_bX.length; i++){ if (this.arr_bY[i] + 1 == this.height){ //Whether the lower boundary is crossed during deformation return false } if (this.arr_store_X.length != 0) { //Determine whether the blocks collide (lower) for(let j = 0; j < this.arr_store_X.length; j++ ){ if (this.arr_bX[i] == this.arr_store_X[j]) { if (this.arr_bY[i] + 1 == this.arr_store_Y[j]) { return false } } } } } return true } //Judge whether the blocks collide (left and right), and whether they cross the left and right boundaries during deformation judgeCollision_other(num){ for(let i = 0; i < this.arr_bX.length; i++){ if (num == 1) { //Whether the right boundary is crossed during deformation if (this.arr_bX[i] == this.width - 1) return false } if (num == -1) { //Whether the left boundary is crossed during deformation if (this.arr_bX[i] == 0) return false } if (this.arr_store_X.length != 0) { / /Determine whether the blocks collide (left and right) for(let j = 0; j < this.arr_store_X.length; j++){ if (this.arr_bY[i] == this.arr_store_Y[j]) { if (this.arr_bX[i] + num == this.arr_store_X[j]) { return false } } } } } return true; } //The acceleration function for down_speed_up( ){ let flag_all_down = true flag_all_down = this.judgeCollision_down() if (flag_all_down) { this.initBackground() for(let i = 0; i < this.arr_bY.length; i++){ this.arr_bY[i] = this.arr_bY[i] + 1 } } else{ for(let i=0; i < this.arr_bX.length; i++){ this.arr_store_X.push(this.arr_bX[i]) this.arr_store_Y.push(this.arr_bY[i]) this.arr_store_color.push(this.type_color) } this.arr_bX.splice(0,this.arr_bX.length) this.arr_bY.splice(0,this.arr_bY. length) this.initBlock() } this.clearUnderBlock() this.drawBlock(this.type_color) this.drawStaticBlock() this.gameover() } //The direction key is the left movement function move(dir_temp){ this.initBackground() if (dir_temp == 1) { //Right let flag_all_right = true flag_all_right = this.judgeCollision_other(1) if (flag_all_right) { for(let i = 0; i < this.arr_bY.length; i++){ this.arr_bX[i] = this.arr_bX[i]+1 } } } else{ let flag_all_left = true flag_all_left = this.judgeCollision_other(-1) if (flag_all_left) { for(let i=0; i < this.arr_bY.length; i++){ this.arr_bX[i] = this.arr_bX[i]-1 } } } this.drawBlock(this.type_color) this.drawStaticBlock() } //The direction key is a space transformation direction function up_change_direction(num_blcok){ if (num_blcok == 5) { return } let arr_tempX = [] let arr_tempY = [] //Because I don’t know whether the transformation can be successful, I store it first for(let i = 0;i < this.arr_bX.length; i++){ arr_tempX.push(this.arr_bX[i]) arr_tempY.push(this.arr_bY[i]) } this.direction++ //Extract the center coordinates and deform them by The current center shall prevail let ax_temp = this.arr_bX[0] let ay_temp = this.arr_bY[0] this.arr_bX.splice(0, this.arr_bX.length) //Clear the array this.arr_bY.splice(0, this.arr_bY.length) if (num_blcok == 1) { switch(this.direction%4){ case 1: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp+1,ay_temp+1,ay_temp+1) break case 2: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp) this.arr_bY.push(ay_temp,ay_temp, ay_temp-1,ay_temp+1) break case 3: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp+1,ay_temp) break case 0: this.arr_bX.push(ax_temp,ax_temp,ax_temp, ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp) break } } if (num_blcok == 2) { switch(this.direction%4){ case 1: this.arr_bX.push(ax_temp,ax_temp -1,ax_temp-1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp-1,ay_temp) break case 2: this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp-1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+ 1,ay_temp+1) break case 3: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp+1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp+1) break case 0: this.arr_bX.push(ax_temp,ax_temp, ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp-1) break } } if (num_blcok == 3) { switch(this.direction%4){ case 1: this.arr_bX.push(ax_temp ,ax_temp-1,ax_temp+1,ax_temp+2) this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp) break case 2: this.arr_bX.push(ax_temp,ax_temp,ax_temp,ax_temp) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp+ 2) break case 3: this.arr_bX.push(ax_temp,ax_temp-1,ax_temp+1,ax_temp+2) this.arr_bY.push(ay_temp,ay_temp,ay_temp,ay_temp) break case 0: this.arr_bX.push(ax_temp,ax_temp,ax_temp, ax_temp) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp+1,ay_temp+2) break } } if (num_blcok == 4) { switch(this.direction%4){ case 1: this.arr_bX.push(ax_temp ,ax_temp-1,ax_temp,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp+1,ay_temp+1) break case 2: this.arr_bX.push(ax_temp,ax_temp,ax_temp+1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp+ 1,ay_temp,ay_temp-1) break case 3: this.arr_bX.push(ax_temp,ax_temp,ax_temp-1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp,ay_temp-1) break case 0: this.arr_bX.push(ax_temp,ax_temp, ax_temp+1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp-1,ay_temp,ay_temp+1) break } } if (! (this.judgeCollision_other(-1) && this.judgeCollision_down() && this.judgeCollision_other(1) )) { // If the transformation is unsuccessful, execute the following code this.arr_bX.splice(0, this.arr_bX.length) this.arr_bY.splice(0, this.arr_bY.length) for(let i=0; i< arr_tempX.length; i++){ this.arr_bX.push(arr_tempX[i]) this.arr_bY.push(arr_tempY[i ]) } } this.drawStaticBlock() } //When a row is full, clear the blocks, and the Y coordinate of the upper block is +1 clearUnderBlock(){ //Delete low-level blocks let arr_row=[] let line_num if (this.arr_store_X.length != 0) { for(let j = this.height-1; j >= 0; j--){ for (let i = 0; i < this.arr_store_color.length; i++){ if (this.arr_store_Y[i] == j) { arr_row.push(i) } } if (arr_row.length == this.width) { line_num = j break }else{ arr_row.splice(0, arr_row.length) } } } if (arr_row.length == this.width ) { //Calculate grade grade this.grade++ document.getElementById('text').innerHTML = 'Current grade:'+this.grade for(let i = 0; i < arr_row.length; i++){ this.arr_store_X.splice(arr_row[i]-i, 1) this.arr_store_Y.splice(arr_row[i]-i, 1) this.arr_store_color. splice(arr_row[i]-i, 1) } //Let the upper block drop one space down for(let i = 0; i < this.arr_store_color.length; i++){ if (this.arr_store_Y[i] < line_num) { this.arr_store_Y[i] = this.arr_store_Y[i]+1 } } } //Judge the end of the game gameover(){ for (let i=0; i < this.arr_store_X.length; i++){ if (this.arr_store_Y[i] == 0) { clearInterval(this.ident) this.over = true } } } } let tetrisObj = new tetris() tetrisObj.gameStart() //Direction key function document.onkeydown = (e) => { if (tetrisObj.over) return switch(e.keyCode){ case 40: // The direction is down tetrisObj.down_speed_up() break case 32: // Space to change direction tetrisObj.initBackground() // Redraw the map tetrisObj.up_change_direction(tetrisObj.num_blcok) tetrisObj.drawBlock(tetrisObj.type_color) break case 37: // The direction is left tetrisObj.initBackground() tetrisObj.move (-1) tetrisObj.drawBlock(tetrisObj.type_color) break case 39: // The direction is right tetrisObj.initBackground() tetrisObj.move(1) tetrisObj.drawBlock(tetrisObj.type_color) break } } </script></body></html >
The above is the entire content of this article. I hope it will be helpful to everyone’s study. I also hope everyone will support VeVb Wulin Network.