1 VTs4Kih4hn9GwCYCXGmIw
Tetris no escuro Eu desenvolvi para comemorar 35 anos de Tetris!

Tetris foi criado por Alexey Pajitnov e lançado em 6 de junho de 1984.

Então como você fazer Tetris em 🍦 JavaScript baunilha do princípio?

Sem bibliotecas. Sem estruturas. Apenas JavaScript e seus dedos de codificação.

Fui um pouco criativo e adicionei Tetris no escuro efeito. Espero que isso torne este tutorial um pouco menos chato. Além disso, abordaremos tudo o que você precisa saber sobre como colocar Tetris juntos em JavaScript.

1 YyQeBavIG2nyIckcVrv25g
Tetris No Escuro.

Tetris é mais simples de jogar do que fazer. Quando comecei a criar o Tetris, aprendi que havia várias tarefas que eu precisava concluir isoladamente. Ou seja, eles são animação, detecção de colisão e a algoritmo de limpeza de linhas.

É assim que minha versão do jogo se parecia na minha primeira tentativa:

1 w2LjHtw7cyz5BL75P4UnYw 1
Primeira tentativa de fazer Tetris.

Eu também queria criar um poço de tamanho dinâmico. Significando que o tamanho da área de jogo pode ser ajustado. o bem e blocos de tetromino pode ser representado por matrizes. Eventos do teclado podem ser usados ​​para receber a entrada do usuário.

Facilitei a mudança de cores, armazenando-as em uma variável global cor. O uso de variáveis ​​globais geralmente é evitado. Mas estamos apenas criando uma demo simples do Tetris aqui? JavaScript baunilha. As cores globais podem ser armazenadas da seguinte maneira:

deixei color = { fundo: “# 5c2a3b”, // plano de fundo
parede: “# d83c66”, // paredes
sólido: “# 49b5ab”, // tetromino sólido
tetromino: “# e97539”}; // caindo tetromino

Para alterar o tema da cor, simplesmente modifique essas cores. Para esta demonstração, todos os tetrominos compartilharão a mesma cor. Mas isso pode ser ajustado mais tarde. Eu só não quero complicar demais o código, para que os principais princípios permaneçam claros.

Código fonte completo do Tetris

Este tutorial é baseado no código Tetris existente que escrevi há uma semana. Você pode bifurcar o Código fonte completo do Tetris do meu Perfil do GutHub. * Eu não vou listar inteira código-fonte neste tutorial para evitar redundância. Mas todas as funções importantes serão listadas aqui.

Antecedentes do projeto. Não, este não é o melhor e mais otimizado jogo de Tetris escrito em JavaScript. Nem é o único jeito para programá-lo em JavaScript. Usei construções simples (matrizes e for-loops) para facilitar a visualização de como tudo se encaixa. Otimizações podem ser aplicadas posteriormente. Este não é um tutorial sobre como escrever o jogo Tetris mais eficiente. Pelo contrário, sobre os princípios por trás disso.

A animação do jogo em uma grade relativamente pequena que consiste em quadrados coloridos pode ser obtida usando elementos DIV dinâmicos. Nem precisamos usar tela. Mas também não há nada que nos impeça de implementá-lo na tela.

Um grande número de jogos é baseado em grade. Você pode se ramificar dessa configuração para criar qualquer jogo simples que ocorra em uma grade 2D: xadrez, Candy Crush ou um simulador de agricultura como Stardew Valley por exemplo.

Bem

10 por 20 é o tamanho clássico do poço Tetris. Mas pode ser de qualquer tamanho. Nesta demonstração, também temos paredes que fazem parte do conjunto de poços. Então, mesmo que o poço esteja 10 quadrados de largura, com as paredes na verdade 12:

1 VJHNdt0qK9YP7DDIvvS45Q

Escreverei o código de forma a permitir que você especifique suas próprias dimensões do poço, independentemente de quão fino ou largo ele seja:

1 K6R5NdYi
50 x 36

Por exemplo, você pode criar uma versão do Tetris com um poço de 50 quadrados de largura e 36 quadrados de profundidade (às vezes é divertido experimentar).

deixei largura = 50; // largura do poço
deixei altura = 36; // altura do poço
deixei square_size = 16; // tamanho quadrado em pixels

Vamos criar a matriz do poço:

deixei bem = Novo Matriz (altura); // matriz segurando o poço inteiro

Essa matriz manterá uma lista de matrizes. (criado na seção a seguir).

Gerando o poço

Cada quadrado no poço terá seu próprio código.

A idéia básica é que 0 é espaço vazio.

Todo o resto (1, 2, 3, etc) é considerado sólido (colidível.)

Para gerar espaço vazio (0), paredes esquerda e direita (1) e inferior (2), tudo o que precisamos fazer é atribuir esses valores ao parâmetro wall matriz em locais adequados:

// Reset entire well to all 0'sfor (let y = 0; y 

The code to create an HTML element is shown below. It is then inserted into element. That’s why our starting HTML document is pretty much empty — all blocks are generated and inserted into DOM dynamically:

// Generate well on the screen by creating HTML elements dynamicallyfor (let y = 0; y  dynamically        document.body.appendChild( square );    }}

Essa função percorre todo o poço, verificando o valor em well[x][y]e gerando um elemento DIV para cada quadrado individualmente. Em seguida, atribui uma cor a esse quadrado com base no valor armazenado em block_type variável, que simplesmente aponta para o valor em well[x][y]

Tetrominos

Os 7 padrões clássicos que consistem em 4 quadrados são chamados tetrominos.

O conjunto clássico de sete tetrominos.

Observe que o bastão longo é o único tetromino que não se encaixa na caixa 3x3:

1 mDMc7i2sLVMpTfamNh1KkQ
O manche é um caso de bola ímpar, pois é o único tetromino que requer uma grade 4x4.

Podemos lidar com esse stick separadamente ou podemos coloque todos os tetrominos em uma matriz 4x4. Existem várias maneiras de lidar com esse problema. Para este tutorial, simplesmente utilizarei um manípulo mais curto que se encaixa em uma caixa 3x3.

Representando um tetromino usando uma matriz JavaScript:

image 40

Para realmente representar um dos tetrominos, você especificará suas partes sólidas usando o valor 1 em vez de 0:

1 SNv4EQc1p5YO08tGpAuemw
Uma maneira de definir um tetromino usando uma matriz unidimensional.

Você pode definir todos os tetrominos dessa maneira e depois colocá-los em uma matriz que representa uma lista inteira contendo todos eles. Se você quer ser criativo, pode até criar suas próprias formas:

let A = [0,0,1,         0,0,1,         0,1,1];let B = [1,0,0,         1,0,0,         1,1,0];let C = [0,0,0,         0,1,0,         1,1,1];let D = [0,0,0,         0,1,1,         1,1,0];let E = [0,0,0,         1,1,0,         0,1,1];let F = [1,1,0,         1,1,0,         0,0,0];let G = [0,0,0,         1,1,1,         0,0,0];

Em seguida, coloco todos os tetrominos em outra matriz:

let tetrominos = [A,B,C,D,E,F,G];

Dessa forma, podemos gerar um tetromino aleatório usando Math.rand função.

Também precisaremos de espaços reservados para o tetromino "próximo" e "atual". Eles são chamados atual e Próximo respectivamente:

let current = [0,0,0, 0,0,0, 0,0,0];let next    = [0,0,0, 0,0,0, 0,0,0];

Aqui está a função que gera um tetromino aleatório:

// Generate a random tetromino and return it as 3x3 arrayfunction make_random() {    // 1.) Select random tetromino from tetrominos array by index    let index = Math.floor((Math.random() * tetrominos.length));    // 2.) Copy it into current array (avoid reference assignment)    return [...tetrominos[ index ]];}

Aqui eu usei… (descansar / espalhar sintaxe) para criar uma cópia de uma matriz. Se simplesmente atribuir essa matriz para uma variável que criaria um referência ao tetromino original. Não queremos uma referência. Então, em vez disso, faremos uma cópia separada na memória. Quando voltarmos [...tetrominos[index]] estamos fazendo uma cópia de de um dos tetrominos do nosso tetrominos[] matriz criada anteriormente.

Para gerar um tetromino aleatório e armazená-lo na variável atual ou na próxima:

current = make_random();next = make_random();

Uma vez que o tetromino esteja permanentemente preso no poço depois de ter caído, podemos trocar atual com Próximo 1.

Controles do teclado

Aqui está o código fonte dos controles do teclado:

// Keyboard inputdocument.addEventListener("keydown", (e) => {    let key_code = e.keyCode;    // Erase the teetromino    erase();    // Left    if (key_code == 37) {        if (will_collide(dir.LEFT)) {            reset();        } else position.x -= 1    }    // Right    if (key_code == 39) {        if (will_collide(dir.RIGHT)) {            reset();        } else position.x += 1    }    // Down    if (key_code == 40) {        if (will_collide(dir.DOWN)) {            reset();        } else position.y += 1    }    if (key_code == 38) { position.y -= 1 }    // Rotate    if (key_code == 90) { rotate_left() }    if (key_code == 88) { rotate_right(); }    // Draw the current tetromino    draw();});

Animação em queda

O ciclo do jogo consiste em apagar, cair e desenhar funções.

É isso que cria a ilusão de um bloco em queda.

// Game-loop AnimationsetInterval(() => {    // Erase the current tetromino block from the well    erase();    // Progress the tetromino by 1 square down    fall();    // Draw the tetromino at its new fallen position    draw();}, 15);

É comum usar setInterval função em jogos. Mas nunca foi destinado a atualizar a tela. Na verdade, é um pouco instável. A única razão pela qual você não percebe isso é porque a animação do Tetris é relativamente lenta.

Se estivéssemos fazendo um jogo de ritmo mais acelerado, onde a animação suave é importante, usaríamos requestAnimationFrame em vez disso, sincronizará nossa animação com a taxa de atualização do monitor. No entanto, neste simples jogo de Tetris, provavelmente não faz muito sentido fazer isso porque os resultados serão quase idênticos.

Outro problema é Beira versões abaixo de 17 e Internet Explorer não confiável fogo requestAnimationFrame antes do ciclo de pintura.

Detecção de colisão

Existem dois tipos de colisões no Tetris. Com paredes e com caído tijolos.

A detecção de colisões no Tetris é complicada. Você precisa determinar se o tijolo colidirá com uma parede, o fundo do poço ou outros tetrominos em uma etapa da animação. antes é movido fisicamente para esse local. Porque você deseja que o tetromino seja colocado em cima de outras áreas de bloqueio - não dentro delas.

Para demonstrar o que quero dizer, criei esta animação que mostra os tetrominos caindo no fundo do poço. Ou pintando as paredes em contato. Esse é o situação que você deseja evitar:

1 RHV a2 6z4kls6CE1bT3dQ
Evite escrever a detecção de colisão em "tempo real". Você precisa descobrir se o bloco atual vai colidir em um tempo futuro E SE é movido na direção em que está se movendo no próximo quadro, não no quadro atual. E se houver uma colisão futura, impeça qualquer movimento adicional e "cole" o tijolo no poço como um bloco sólido (o último não é mostrado nesta animação, será explicado em uma das seções a seguir).

Nota: nesta fase, estamos tentando determinar a colisão apenas com paredes e a inferior do poço. Há sim nenhuma colisão entre blocos caídos neste ponto. Nós vamos lidar com isso mais tarde até colando o bloco no poço e marcando-o como sólido.

(Isso será explicado em uma das seguintes seções.)

Para lidar com o problema, podemos escrever uma função para determinar o que acontece com o tetromino no futuro - Se isso estavam mudou um bloco esquerda, direita ou baixa.

Podemos fazer isso interceptando eventos do teclado. Quando uma tecla é pressionada, a função informa se o tetromino movido para a nova posição gerará colisão com as paredes ou outros blocos. Se não houver colisão naquele futuro localização, vamos mover o bloco para lá. Caso contrário, nós colar bloquear no bem matriz, marque-o como bloco sólido e gerar nosso próximo tetromino.

Vamos dar uma olhada neste exemplo isolado:

// Left arrow key is pressedif (key_code == 37) {    // Will tetromino collide with walls or if it is moved left?    if (will_collide( dir.LEFT ))        reset();    else       // Tetromino will not collide, move to that position        position.x -= 1;}

Se a função will_collide retorna true, redefinimos a visualização. o reset()função vai realmente colar o tetromino no poço na posição atual.

Abaixo está uma lista de ambos reset() epaste() funções:

Redefinir()

A função de redefinição é mais uma função auxiliar. Chama colar(), clear_row (), make_random (), update_next () e apaga a névoa da escuridão.

function reset() {    paste();                // paste current tetromino onto the well    clear_row();            // clear rows if any    current = [...next];    // swap current and next tetromino    next = make_random();   // generate next tetromino    update_next();          // Update "next" box    // reset current position to top and middle of the well    position.x = parseInt(width/2) - 1;    position.y = -3;    reset_fog();             // clear the fog of darkness}

colar()

// "paste" current block onto the wellfunction paste() {    let index = 0;    // Prevent pasting blocks that fall outside of the well:    if (position.x >= 0 && position.x = -3 && position.y 

erase()

It’s the same as paste() only it sets the currently falling tetromino to all 0’s, effectively erasing it from the well array (before animating it to next position.)

// "paste" current block onto the wellfunction paste() {    let index = 0;    // Prevent pasting blocks that fall outside of the well:    if (position.x >= 0 && position.x = -3 && position.y 

Pasting The Fallen Block Into The Well

Once a block is considered “fallen” it is physically pasted into the well array.

This solid block becomes a 1 in the well array. This means next time we check next tetromino for collisions (using the same collision detection algorithm we already wrote above) this area will be considered as solid and blocks will collide with other blocks too:

1 bpJKZos2FyqboQ89YbnbPw
Once a brick collides with walls or other bricks, it gets “pasted” into the well and marked as solid.

Row Breaking Algorithm

This is the most complex piece of code when it comes to Tetris. This algorithm will check if 1) there are any rows to clear 2) rebuild the well again without the complete rows to cancel them out and create block collapsing illusion.

In my version I also added a highlight animation to make it visually clear that the blocks were cleared. You’ll see it in the final version of this demo.

It is possible to come up with a crazy optimized version of this function in just few lines of code. But if I did that, the logic would be abstracted and difficult to follow and learn from.

It’s nice to look at the logic by using simple JavaScript constructs like arraysand for-loops. And later, you can optimize it to make the code shorter.

// Check if a row needs to be clearedfunction clear_row() {    // Placeholder for new rows    let placeholder = [];    // How many rows cleared?    let rows_cleared = 0;    // Scan the well one row at a time and capture any    // non-filled rows in placeholder    // (except the last row)    for (let y = 0; y  0) {        // Clear the well, except last row (well's bottom)        for (let y = 0; y  0; i--) {            let row = placeholder[i];            for (let x = 0; x 

Tetris In The Dark (Included in source code!)

Strategy video games have something called for of war. It covers an unexplored area of terrain with blackness.

Adding Light

To create a light spot, first I simply created a secondary grid sharing the same dimensions as the well and used it as an overlay. By default all DIV squares on that grid were set to black color and opacity of 1.

Each square on secondary grid was assigned id of fog_x1y3 (if you wanted to access the square at x=1 and y=3 on the said grid of darkness.)

Then I stored my light spot data in a separate array:

// light positionlet light = { x: 0, y: 0 };// lightspot datalet light_mask = [    0,0,0,0,0,0,0,0,0,0,0,0,0,0,    0,0,0,0,0,0,4,4,0,0,0,0,0,0,    0,0,0,2,3,4,5,5,4,3,2,0,0,0,    0,0,2,3,4,5,6,6,5,4,3,2,0,0,    0,2,3,4,5,6,7,7,6,5,4,3,2,0,    0,3,4,5,6,7,8,8,7,6,5,4,3,0,    0,4,5,6,7,8,9,9,8,7,6,5,4,0,    0,4,5,6,7,8,9,9,8,7,6,5,4,0,    0,3,4,5,6,7,8,8,7,6,5,4,3,0,    0,2,3,4,5,6,7,7,6,5,4,3,2,0,    0,0,2,3,4,5,6,6,5,4,3,2,0,0,    0,0,0,2,3,4,5,5,4,3,2,0,0,0,    0,0,0,0,0,0,4,4,0,0,0,0,0,0,    0,0,0,0,0,0,0,0,0,0,0,0,0,0,];

Aqui, valores entre 0 e 9 representam 0,0 - 0,9 opacidade do CSS.

Colei esses dados na grade escura usando um loop for simples (da mesma maneira que colei um tetromino no poço mais cedo), desta vez usando draw_light ()função:

função draw_light () {    deixe índice = 0;    for (deixe y = 0; y 

Isso criou a ilusão de luz desaparecendo na escuridão.

A animação de quebra de linha é feita separadamente de todo o resto. É apenas uma lista de elementos DIV horizontais longos em todos os níveis de altura do poço.

Quando uma linha precisa ser quebrada, o clear_row () A função (mostrada anteriormente) rastreia a coordenada Y de cada linha que precisa ser limpa.

Então uma setInterval A função determina se o estado desse DIV é 1. Se for, ele define o DIV para a cor de fundo branco e reproduz uma animação reduzindo a opacidade ao longo do tempo:

1 XIqveUj5SsZ1Ko01HfRQQQ

Ao trabalhar com o mesmo assunto por um longo período, você tende a ficar um pouco entediado e o impulso de inovar desperta. Tetris In The Dark nasceu! Como desenvolvedor de jogos, decidi criar uma versão alternativa do Tetris adicionando neblina de guerra. Nem todas as experiências são boas. Mas quando se trata de tetris no escuro, estou bastante satisfeito com os resultados.

Você quer aprender a fazer seus próprios jogos?

Verificação de saída meu livro JavaScript Grammar para aprender e aprender JavaScript.

Ou simplesmente Siga me no twitter para codificar anúncios de tutoriais e muito mais.

Me siga @ Twitter, Instagram & fb para nunca perder artigos premium.

alguma coisa