A primeira vez que escrevi Tetris foi há mais de um ano e acabei de aprender js há pouco tempo.
Para fortalecer minha compreensão de js e agregar meu amor por jogos, escrevi Tetris baseado em canvas usando o código js mais básico sem me referir a ideias e códigos de outras pessoas.
Durante as férias de verão do meu primeiro ano, usei a sintaxe do es6 para melhorá-lo, incluindo açúcar sintático de classe, funções de seta, etc., para aumentar ainda mais minha compreensão do es6. O código tem mais de 400 linhas.
Se você quiser fazer este joguinho, primeiro você deve estar familiarizado com o canvas H5, processamento de array js, monitoramento e processamento de eventos de teclado, uso de timer, etc.
As regras do jogo são o núcleo e a principal prioridade do nosso código
A lógica central aqui é determinar se o bloco colide (o bloco atualmente em movimento colide com o bloco já posicionado, de modo que o bloco atualmente em movimento não pode se mover para baixo, porque nosso bloco se move para baixo por padrão. Se não puder se mover para baixo, caminhar para baixo é considerado como um bloco que foi posicionado, e então um novo bloco é gerado e continua a descer da posição inicial).
E esta colisão também precisa ser aplicada quando o bloco é deformado. Da mesma forma, se o bloco colidir com outros blocos posicionados durante o processo de deformação, devemos evitar que o bloco se deforme com sucesso.
O código está anexado, discussões e correções são bem-vindas.
<!DOCTYPE html><html lang=en><head> <meta charset=UTF-8> <title>es6-Reconstrução do Tetris (baseado em canvas)</title> <style type=text/css> #tetris{ margem: 10px 250px;} </style></head><body> <canvas width=700 height=525 id=tetris></canvas> <div id=text style='color: red;font-size: 30px;'>Pontuação atual: 0</div> <script type=text/javascript> /** * [Um design completo de classe Tetris por magic_xiang] * @param {número} lado [comprimento de cada lado quadrado (px), padrão 35] * @param {número} largura [número de quadrados em uma linha (número), padrão 20] * @param {número} altura [Número de blocos incluídos em uma coluna (padrão 15)] * @param {número} velocidade [Velocidade de movimento de queda do bloco (ms), padrão 400] */ class tetris{ construtor(lado=35, largura=20, altura=15 , speed=400){ this.side = side // O comprimento lateral de cada bloco this.width = width // O número de blocos contidos em uma linha this.height = height // O número de blocos contidos em uma coluna this .velocidade = velocidade // A velocidade de movimento de queda do bloco this.num_blcok // A variável numérica do tipo de bloco atual this.type_color // A variável string do tipo de cor atual this.ident // O identificador de setInterval this.direction = 1 // O direção do bloco, inicializado em 1, status padrão this.grade = 0 // Usado para calcular pontuações this.over = false // Se o jogo acabou this.arr_bX = [] // Armazena a coordenada X do bloco atual this.arr_bY = [] // Armazena a coordenada Y do bloco atual this.arr_store_X = [] // Armazena a coordenada X de todos os blocos que atingiram o fundo this.arr_store_Y = [] // Armazena a coordenada Y de todos os blocos que atingiram o fundo this. arr_store_color = [] // Armazena todos os blocos que atingiram o fundo A cor do quadrado this.paints = document.getElementById('tetris').getContext('2d') //Obtém o pincel self = this } // Encapsule o método paints para tornar o código mais conciso paintfr(x, y, scale=1){ this.paints.fillRect(x*this.side, y*this.side, scale*this.side, scale*this.side ) } // O jogo começa gameStart(){ this.init() this.run() } // Trabalho de inicialização init(){ this.initBackground() this.initBlock() } // O bloco cai automaticamente run(){ this.ident = setInterval(self.down_speed_up(), this.speed) } // Inicializa o mapa initBackground(){ this.paints.beginPath() this.paints.fillStyle='#000000 ' //A cor de preenchimento do mapa é preta for(let i = 0; i < this.height; i++){ for(let j = 0; j < this.width; j++){ this.paintfr(j, i) } } this.paints.closePath() } // Inicializa a posição e a cor do bloco initBlock(){ this.paints.beginPath() this.createRandom('rColor') // Gera uma string de cores, this.paints.fillStyle = this.type_color this.createRandom('rBlock') //Gerar números de tipo de bloco this.arr_bX.forEach((item, index) => { this.paintfr(item, this.arr_bY[index], 0.9) }) this.paints.closePath() } // Usar array para desenhar Bloco 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() } // Desenha o bloco já posicionado drawStaticBlock( ){ this.arr_store_X.forEach((item, í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() }) } // Gera um número aleatório e retorna o tipo ou cor do bloco digite createRandom(type){ deixe temp = this.width/2-1 if (type == 'rBlock'){ //Se for um bloco digite 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) quebrar caso 2: this.arr_bX.push(temp,temp-1,temp-1,temp+1) this.arr_bY.push(1,0,1,1) quebra caso 3: this.arr_bX.push(temp,temp-1,temp+1,temp+2) this.arr_bY.push(0,0,0, 0) caso de interrupção 4: this.arr_bX.push(temp,temp-1,temp,temp+1) this.arr_bY.push(0,0,1,1) caso de interrupção 5: this.arr_bX.push(temp,temp+1,temp,temp+1) this.arr_bY.push(0,0,1,1) break } } if (type == 'rColor'){ //se É o tipo de cor 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' caso de interrupção 3: this.type_color='#2FE0BF' caso de interrupção 4: this.type_color='red' caso de interrupção 5: this.type_color='gray' caso de interrupção 6: this.type_color ='#C932C6' quebra caso 7: this.type_color= '#FC751B' quebra caso 8: this.type_color= '#6E6EDD' break case 9: this.type_color= '#F4E9E1' break } } } // Determine se os blocos colidem (inferior) e se eles cruzam o limite inferior durante a deformação juizCollision_down(){ for(let i = 0 ; i < this.arr_bX.length; i++){ if (this.arr_bY[i] + 1 == this.height){ //Se o limite inferior é cruzado durante a deformação, retorne false } if (this.arr_store_X.length != 0) { //Determina se os blocos colidem (inferior) 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 } //Julga se os blocos colidem (esquerda e direita) e se eles cruzam os limites esquerdo e direito durante a deformação juizCollision_other(num){ for(let i = 0; i < this.arr_bX.length; i++){ if (num == 1) { //Se o limite direito é cruzado durante a deformação if (this.arr_bX[i] == this.width - 1) return false } if (num == -1) { //Se o limite esquerdo é cruzado durante a deformação if (this.arr_bX[i] == 0) return false } if (this.arr_store_X.length != 0) { //Determine se os blocos colidem (esquerda e direita) 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 } //A função de aceleração down_speed_up(){ let flag_all_down; = verdadeiro flag_all_down = this.judgeCollision_down() if (flag_all_down) { this.initBackground() for(deixe i = 0; i < this.arr_bY.length; i++){ this.arr_bY[i] = this.arr_bY[i] + 1 } } else{ for(deixe i=0; i <este.arr_bX.comprimento; i++){ este.arr_store_X.push(este.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. comprimento) this.initBlock() } this.clearUnderBlock() this.drawBlock(this.type_color) this.drawStaticBlock() this.gameover() } //A tecla de direção é a função de movimento para a esquerda move(dir_temp){ this.initBackground() if (dir_temp == 1) { //Direita deixe flag_all_right = true flag_all_right = this.judgeCollision_other(1) if (flag_all_right) { for(deixe 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; eu < este.arr_bY.comprimento; i++){ este.arr_bX[i] = this.arr_bX[i]-1 } } } this.drawBlock(this.type_color) this.drawStaticBlock() } //A chave de direção é uma função de direção de transformação de espaço up_change_direction(num_blcok){ if (num_blcok == 5) { return } let arr_tempX = [] let arr_tempY = [] //Como não sei se a transformação pode ser bem-sucedida, armazeno-a primeiro 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++ //Extraia as coordenadas do centro e deforme-as por The o centro atual prevalecerá, deixe ax_temp = this.arr_bX[0] deixe ay_temp = this.arr_bY[0] this.arr_bX.splice(0, this.arr_bX.length) //Limpa a 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) quebrar caso 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 interrupção 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) quebrar caso 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,ax_temp-1,ax_temp+1) this.arr_bY.push(ay_temp,ay_temp,ay_temp-1,ay_temp) quebrar caso 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) caso de interrupção 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) quebrar caso 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) quebrar caso 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 ) quebra o caso 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) quebrar caso 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) quebrar caso 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 interrupção 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) quebra caso 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) )) { // Se a transformação não for bem-sucedida, execute o seguinte 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() } //Quando uma linha estiver cheia, limpe os blocos e a coordenada Y do bloco superior é +1 clearUnderBlock(){ //Excluir blocos de baixo nível let arr_row=[] let line_num if (this.arr_store_X.length != 0) { for(let j = this.height-1; j >= 0; j--) { for (deixe 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 nota nota this.grade++ document.getElementById('text').innerHTML = 'Nota atual:'+this.grade for(deixe 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) } //Deixe o bloco superior cair um espaço para baixo 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 } } } //Julgar o final do jogo gameover() { for (deixe 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() //Função da tecla de direção document.onkeydown = (e) => { if (tetrisObj.over) return switch(e.keyCode){ case 40: // A direção é para baixo tetrisObj.down_speed_up() break case 32: // Espaço para mudar de direção tetrisObj.initBackground() // Redesenhar o mapa tetrisObj.up_change_direction(tetrisObj.num_blcok) tetrisObj.drawBlock(tetrisObj.type_color) break case 37: // A direção é esquerda tetrisObj.initBackground() tetrisObj .mover (-1) tetrisObj.drawBlock(tetrisObj.type_color) break case 39: // A direção é certa tetrisObj.initBackground() tetrisObj.move(1) tetrisObj.drawBlock(tetrisObj.type_color) break } } </script></body></ HTML >
O texto acima é todo o conteúdo deste artigo. Espero que seja útil para o estudo de todos. Também espero que todos apoiem a Rede VeVb Wulin.