La primera vez que escribí Tetris fue hace más de un año y no hace mucho que aprendí js.
Para fortalecer mi comprensión de JS y agregar mi amor por los juegos, escribí Tetris basado en lienzo usando el código JS más básico sin hacer referencia a las ideas y códigos de otras personas.
Durante las vacaciones de verano de mi tercer año, utilicé la sintaxis de es6 para mejorarlo, incluido el azúcar sintáctico de clase, funciones de flecha, etc., para aumentar aún más mi comprensión de es6. El código tiene más de 400 líneas.
Si quieres crear este pequeño juego, primero debes estar familiarizado con el lienzo H5, el procesamiento de matrices js, el monitoreo y procesamiento de eventos del teclado, el uso del temporizador, etc. El resto es procesamiento lógico básico.
Las reglas del juego son el núcleo y la máxima prioridad de nuestro código.
La lógica central aquí es determinar si el bloque choca (el bloque que se mueve actualmente choca con el bloque ya posicionado, de modo que el bloque que se mueve actualmente no puede moverse hacia abajo, porque nuestro bloque se mueve hacia abajo de forma predeterminada. Si no puede moverse hacia abajo, se considera caminar hacia abajo). como un bloque que ha sido posicionado, y luego se genera un nuevo bloque y continúa descendiendo desde la posición inicial).
Y esta colisión también debe aplicarse cuando el bloque se deforma. De manera similar, si el bloque choca con otros bloques posicionados durante el proceso de deformación, debemos evitar que el bloque se deforme con éxito.
Se adjunta el código, las discusiones y correcciones son bienvenidas.
<!DOCTYPE html><html lang=es><head> <meta charset=UTF-8> <title>es6-Reconstrucción de Tetris (basado en lienzo)</title> <style type=text/css> #tetris{ margen: 10px 250px;} </style></head><body> <canvas width=700 height=525 id=tetris></canvas> <div id=text style='color: red;font-size: 30px;'>Puntuación actual: 0</div> <script type=text/javascript> /** * [Un diseño completo de clase de Tetris por magic_xiang] * @param {número} lado [longitud de cada lado del cuadrado (px), predeterminado 35] * @param {número} ancho [número de cuadrados en una fila (número), predeterminado 20] * @param {número} alto [Número de bloques incluidos en una columna (predeterminado 15)] * @param {número} velocidad [Velocidad de movimiento de caída del bloque (ms), predeterminado 400] */ class tetris{ constructor(lado=35, ancho=20, alto=15 , speed=400){ this.side = side // La longitud del lado de cada bloque this.width = width // El número de bloques contenidos en una fila this.height = height // El número de bloques contenidos en una columna this .velocidad = velocidad // La velocidad de movimiento de caída del bloque this.num_blcok // La variable numérica del tipo de bloque actual this.type_color // La variable de cadena del tipo de color actual this.ident // El identificador de setInterval this.direction = 1 // El dirección del bloque, inicializada en 1, estado predeterminado this.grade = 0 // Se utiliza para calcular puntuaciones this.over = false // Si el juego terminó this.arr_bX = [] // Almacena la coordenada X del bloque actual this.arr_bY = [] // Almacena la coordenada Y del bloque actual this.arr_store_X = [] // Almacena la coordenada X de todos los bloques que han llegado al final this.arr_store_Y = [] // Almacena la coordenada Y de todos los bloques que han llegado al final this. arr_store_color = [] // Almacena todos los bloques que han llegado al fondo El color del cuadrado this.paints = document.getElementById('tetris').getContext('2d') //Obtiene el pincel self = this } // Encapsule el método paints para hacer el código más conciso paintfr(x, y, scale=1){ this.paints.fillRect(x*this.side, y*this.side, scale*this.side, scale*this.side ) } // El juego comienza gameStart(){ this.init() this.run() } // Trabajo de inicialización init(){ this.initBackground() this.initBlock() } // El bloque cae automáticamente run(){ this.ident = setInterval(self.down_speed_up(), this.speed) } // Inicializa el mapa initBackground(){ this.paints.beginPath() this.paints.fillStyle='#000000 ' //El color de relleno del mapa es negro for(let i = 0; i < this.height; i++){ for(let j = 0; j < this.width; j++){ this.paintfr(j, i) } } this.paints.closePath() } // Inicializa la posición y el color del bloque initBlock(){ this.paints.beginPath() this.createRandom('rColor') // Genera una cadena de color, this.paints.fillStyle = this.type_color this.createRandom('rBlock') //Generar números de tipo de bloque this.arr_bX.forEach((item, index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // Usar matriz para dibujar bloque dibujarBloque(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() } // Dibuja el bloque ya posicionado drawStaticBlock(){ this.arr_store_X.forEach((elemento, índice) => { this.paints.beginPath() this.paints.fillStyle = this.arr_store_color[index] this.paintfr(item, this.arr_store_Y[index], 0.9) this.paints.closePath() }) } // Genera un número aleatorio y devuelve el tipo o color del bloque escriba createRandom(tipo){ let temp = this.width/2-1 if (tipo == 'rBlock'){ //Si es un bloque, escriba this.num_blcok = Math.round(Math.random()*4+1) switch(this.num_blcok){ caso 1: this.arr_bX.push(temp,temp-1,temp, temp+ 1) this.arr_bY.push(0,1,1,1) romper caso 2: this.arr_bX.push(temp,temp-1,temp-1,temp+1) this.arr_bY.push(1,0,1,1) romper caso 3: this.arr_bX.push(temp,temp-1,temp+1,temp+2) this.arr_bY.push(0,0,0, 0) caso de ruptura 4: this.arr_bX.push(temp,temp-1,temp,temp+1) this.arr_bY.push(0,0,1,1) caso de ruptura 5: this.arr_bX.push(temp,temp+1,temp,temp+1) this.arr_bY.push(0,0,1,1) break } } if (type == 'rColor'){ //if Es el tipo de color let num_color = Math.round(Math.random()*8+1) switch(num_color){ case 1: this.type_color='#3EF72A' break case 2: this.type_color='amarillo' caso de ruptura 3: this.type_color='#2FE0BF' caso de ruptura 4: this.type_color='red' caso de ruptura 5: this.type_color='gray' caso de ruptura 6: this.type_color ='#C932C6' caso de ruptura 7: this.type_color= '#FC751B' caso de ruptura 8: this.type_color= '#6E6EDD' break case 9: this.type_color= '#F4E9E1' break } } } // Determina si los bloques chocan (inferior) y si cruzan el límite inferior durante la deformación JudgeCollision_down(){ for(let i = 0 ; i < this.arr_bX.length; i++){ si (this.arr_bY[i] + 1 == this.height){ //Si se cruza el límite inferior durante la deformación return false } if (this.arr_store_X.length != 0) { //Determina si los bloques chocan (inferiores) for(let j = 0; j < this.arr_store_X.length; j++ ){ si (this.arr_bX[i] == this.arr_store_X[j]) { si (this.arr_bY[i] + 1 == this.arr_store_Y[j]) { return false } } } } } return true } // Juzga si los bloques chocan (izquierda y derecha) y si cruzan los límites izquierdo y derecho durante la deformación JudgeCollision_other(num){ for(let i = 0; i < this.arr_bX.length; i++){ if (num == 1) { //Si se cruza el límite derecho durante la deformación if (this.arr_bX[i] == this.width - 1) return false } if (num == -1) { //Si se cruza el límite izquierdo durante la deformación if (this.arr_bX[i] == 0) return false } if (this.arr_store_X.length != 0) { / /Determine si los bloques chocan (izquierda y derecha) 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 } // La función de aceleración para down_speed_up(){ let; flag_all_down = verdadero 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. longitud) this.initBlock() } this.clearUnderBlock() this.drawBlock(this.type_color) this.drawStaticBlock() this.gameover() } //La tecla de dirección es la función de movimiento hacia la izquierda move(dir_temp){ this.initBackground() if (dir_temp == 1) { //Derecha 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 < este.arr_bY.length; i++){ este.arr_bX[i] = this.arr_bX[i]-1 } } } this.drawBlock(this.type_color) this.drawStaticBlock() } // La clave de dirección es una función de dirección de transformación espacial up_change_direction(num_blcok){ if (num_blcok == 5) { return } let arr_tempX = [] let arr_tempY = [] // Como no sé si la transformación puede tener éxito, la almaceno primero para (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++ //Extrae las coordenadas centrales y deformalas por The prevalecerá el centro actual let ax_temp = this.arr_bX[0] let ay_temp = this.arr_bY[0] this.arr_bX.splice(0, this.arr_bX.length) //Borrar la matriz this.arr_bY.splice(0, this.arr_bY.length) if (num_blcok == 1) { switch(this.direction%4){ caso 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) caso de ruptura 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) caso de ruptura 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) caso de ruptura 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){ caso 1: this.arr_bX.push(ax_temp,ax_temp -1,temp_ax-1,temp_ax+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp-1,ay_temp) caso de ruptura 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) romper caso 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) caso de ruptura 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){ caso 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) caso de ruptura 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 ) caso de ruptura 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) caso de ruptura 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){ caso 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) caso de ruptura 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) caso de ruptura 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) caso de ruptura 0: this.arr_bX.push(ax_temp,ax_temp, temperatura_hacha+1,temp_hacha+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) )) { // Si la transformación no tiene éxito, ejecute el siguiente código 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() } // Cuando una fila está llena, borra los bloques y la coordenada Y del bloque superior es +1 clearUnderBlock(){ //Eliminar bloques de bajo nivel 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 ) { //Calcular calificación calificación this.grade++ document.getElementById('text').innerHTML = 'Calificación actual:'+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) } //Deja que el bloque superior caiga un espacio hacia abajo 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 } } } //Juzga el final del juego () { para (let i=0; i < this.arr_store_X.length; i++){ si (this.arr_store_Y[i] == 0) { clearInterval(this.ident) this.over = true } } } } let tetrisObj = new tetris() tetrisObj.gameStart() // Función de tecla de dirección document.onkeydown = (e) => { if (tetrisObj.over) return switch(e.keyCode){ case 40: // La dirección es hacia abajo tetrisObj.down_speed_up() break case 32: // Espacio para cambiar de dirección tetrisObj.initBackground() // Redibujar el mapa tetrisObj.up_change_direction(tetrisObj.num_blcok) tetrisObj.drawBlock(tetrisObj.type_color) break case 37: // La dirección es hacia la izquierda tetrisObj.initBackground() tetrisObj .mover (-1) tetrisObj.drawBlock(tetrisObj.type_color) break case 39: // La dirección es correcta tetrisObj.initBackground() tetrisObj.move(1) tetrisObj.drawBlock(tetrisObj.type_color) break } } </script></body></ HTML >
Lo anterior es el contenido completo de este artículo. Espero que sea útil para el estudio de todos. También espero que todos apoyen VeVb Wulin Network.