PauloRB.dev

Desenvolvedor de Software

Tutorial de Desenvolvimento: Criando um Jogo de Tetris | PauloRB.dev Tutorial de Desenvolvimento: Criando um Jogo de Tetris – PauloRB.dev
Tutorial de Desenvolvimento: Criando um Jogo de Tetris

Tutorial de Desenvolvimento: Criando um Jogo de Tetris

E vamos a mais um tutorial de outro joguinho clássico só que dessa vez vamos recriar o Tetris.

Link do repositório: https://github.com/paulodm145/tetris

Estrutura do Projeto

/projeto-tetris

├── index.html
├── styles.css
└── script.js

1. index.html

Este arquivo contém a estrutura básica da página do jogo.

<!DOCTYPE html>
<html lang="pt">
<head>
    <meta charset="UTF-8">
    <title>Tetris</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div id="tetris-container">
        <div id="score">Pontuação: <span>0</span></div>
        <div id="tetris"></div>
        <div class="buttons">
            <button id="restart">Reiniciar</button>
            <button id="pause">Pausar</button>
            <button id="reset-score">Zerar Pontuação</button>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

  • Cabeçalho: Contém as configurações básicas e a inclusão do arquivo CSS.
  • Corpo: Inclui o contêiner principal do jogo, o tabuleiro (#tetris), a exibição da pontuação e os botões de controle.

2. styles.css

Este arquivo contém os estilos do jogo.

body {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
    background-color: #e0e0e0;
    font-family: Arial, sans-serif;
    margin: 0;
}

#tetris-container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

#tetris {
    width: 300px;
    height: 600px;
    border: 2px solid #333;
    background-color: #fff;
    display: grid;
    grid-template-columns: repeat(10, 1fr);
    grid-template-rows: repeat(20, 1fr);
    gap: 1px;
    margin-bottom: 20px;
}

#tetris div {
    background-color: #f0f0f0;
    box-sizing: border-box;
}

#score {
    font-size: 1.5em;
    color: #333;
    margin-bottom: 10px;
}

#score span {
    font-weight: bold;
}

.buttons {
    display: flex;
    gap: 10px;
}

button {
    padding: 10px 20px;
    font-size: 1em;
    color: #fff;
    background-color: #007bff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s;
}

button:hover {
    background-color: #0056b3;
}

  • Estilos Básicos: Define o layout da página e do contêiner do jogo.
  • Tabuleiro: Configura o tamanho e o estilo do tabuleiro de Tetris.
  • Botões: Estiliza os botões de controle com cores e transições.

3. script.js

Este arquivo contém a lógica do jogo de Tetris.

document.addEventListener('DOMContentLoaded', () => {
    const grid = document.querySelector('#tetris');
    const width = 10;
    let timerId;
    let score = 0;
    const colors = ['orange', 'red', 'purple', 'green', 'blue'];

    // Criar a grade e preencher com divs
    function createBoard() {
        for (let i = 0; i < 200; i++) { // 200 divs para o tabuleiro
            let square = document.createElement('div');
            grid.appendChild(square);
        }
        for (let i = 0; i < 10; i++) { // 10 divs para a linha de base
            let square = document.createElement('div');
            square.classList.add('taken');
            grid.appendChild(square);
        }
    }
    createBoard();

    let squares = Array.from(grid.querySelectorAll('div'));
    const scoreDisplay = document.querySelector('#score span');
    let nextRandom = 0; // inicialização correta de nextRandom

    // Os Tetrominos e suas rotações
    const lTetromino = [
        [1, width+1, width*2+1, 2],
        [width, width+1, width+2, width*2+2],
        [1, width+1, width*2+1, width*2],
        [width, width*2, width*2+1, width*2+2]
    ];

    const zTetromino = [
        [0, width, width+1, width*2+1],
        [width+1, width+2, width*2, width*2+1],
        [0, width, width+1, width*2+1],
        [width+1, width+2, width*2, width*2+1]
    ];

    const tTetromino = [
        [1, width, width+1, width+2],
        [1, width+1, width+2, width*2+1],
        [width, width+1, width+2, width*2+1],
        [1, width, width+1, width*2+1]
    ];

    const oTetromino = [
        [0, 1, width, width+1],
        [0, 1, width, width+1],
        [0, 1, width, width+1],
        [0, 1, width, width+1]
    ];

    const iTetromino = [
        [1, width+1, width*2+1, width*3+1],
        [width, width+1, width+2, width+3],
        [1, width+1, width*2+1, width*3+1],
        [width, width+1, width+2, width+3]
    ];

    const theTetrominos = [lTetromino, zTetromino, tTetromino, oTetromino, iTetromino];

    let currentPosition = 4;
    let currentRotation = 0;
    let random = Math.floor(Math.random() * theTetrominos.length);
    let current = theTetrominos[random][currentRotation];

    // Desenhar o Tetromino
    function draw() {
        current.forEach(index => {
            squares[currentPosition + index].style.backgroundColor = colors[random];
        });
    }

    // Desfazer o desenho do Tetromino
    function undraw() {
        current.forEach(index => {
            squares[currentPosition + index].style.backgroundColor = '';
        });
    }

    // Fazer o Tetromino cair a cada segundo
    timerId = setInterval(moveDown, 1000);

    // Mover Tetromino para baixo
    function moveDown() {
        undraw();
        currentPosition += width;
        draw();
        freeze();
    }

    // Congelar quando o Tetromino atinge o fundo da grade
    function freeze() {
        if (current.some(index => squares[currentPosition + index + width].classList.contains('taken'))) {
            current.forEach(index => squares[currentPosition + index].classList.add('taken'));
            random = nextRandom;
            nextRandom = Math.floor(Math.random() * theTetrominos.length);
            current = theTetrominos[random][currentRotation];
            currentPosition = 4;
            draw();
            addScore();
            gameOver();
        }
    }

    // Adicionar pontuação
    function addScore() {
        for (let i = 0; i < 199; i += width) {
            const row = [i, i+1, i+2, i+3, i+4, i+5, i+6, i+7, i+8, i+9];

            if (row.every(index => squares[index].classList.contains('taken'))) {
                score += 10;
                scoreDisplay.innerText = score;
                row.forEach(index => {
                    squares[index].classList.remove('taken');
                    squares[index].style.backgroundColor = '';
                });
                const squaresRemoved = squares.splice(i, width);
                squares = squaresRemoved.concat(squares);
                squares.forEach(cell => grid.appendChild(cell));
            }
        }
    }

    // Verificar o fim do jogo
    function gameOver() {
        if (current.some(index => squares[currentPosition + index].classList.contains('taken'))) {
            scoreDisplay.innerText = 'Fim de jogo';
            clearInterval(timerId);
        }
    }

    document.addEventListener('keyup', control);
    function control(e) {
        if (e.keyCode === 37) {
            moveLeft();
        } else if (e.keyCode === 38) {
            rotate();
        } else if (e.keyCode === 39) {
            moveRight();
        } else if (e.keyCode === 40) {
            moveDown();
        }
    }

    function moveLeft() {
        undraw();
        const isAtLeftEdge = current.some(index => (currentPosition + index) % width === 0);
        if (!isAtLeftEdge) currentPosition -= 1;
        if (current.some(index => squares[currentPosition + index].classList.contains('taken'))) {
            currentPosition += 1;
        }
        draw();
    }

    function moveRight() {
        undraw();
        const isAtRightEdge = current.some(index => (currentPosition + index) % width === width - 1);
        if (!isAtRightEdge) currentPosition += 1;
        if (current.some(index => squares[currentPosition + index].classList.contains('taken'))) {
            currentPosition -= 1;
        }
        draw();
    }

    function rotate() {
        undraw();
        currentRotation++;
        if (currentRotation === current.length) {
            currentRotation = 0;
        }
        current = theTetrominos[random][currentRotation];
        draw();
    }

    document.querySelector('#restart').addEventListener('click', () => {
        clearInterval(timerId);
        score = 0;
        scoreDisplay.innerText = score;
        nextRandom = 0;
        createBoard();
        random = Math.floor(Math.random() * theTetrominos.length);
        current = theTetrominos[random][currentRotation];
        currentPosition = 4;
        draw();
        timerId = setInterval(moveDown, 1000);
    });

    document.querySelector('#pause').addEventListener('click', () => {
        if (timerId) {
            clearInterval(timerId);
            timerId = null;
            document.querySelector('#pause').innerText = 'Retomar';
        } else {
            timerId = setInterval(moveDown, 1000);
            document.querySelector('#pause').innerText = 'Pausar';
        }
    });

    document.querySelector('#reset-score').addEventListener('click', () => {
        score = 0;
        scoreDisplay.innerText = score;
    });
});

  • Variáveis e Seletores: Definem elementos do DOM e variáveis necessárias para o jogo.
  • createBoard(): Cria o tabuleiro do jogo preenchendo-o com células.
  • Tetrominos: Define as formas e rotações dos Tetrominos.
  • Desenho e Movimentação: Funções para desenhar, mover, rotacionar e congelar os Tetrominos.
  • Controle de Teclado: Adiciona eventos de teclado para mover e rotacionar os Tetrominos.
  • Funções de Controle: Funções para reiniciar, pausar e zerar a pontuação do jogo.

Como Executar

  1. Faça o download ou clone este repositório.
  2. Abra o arquivo index.html em um navegador web.
  3. O jogo será carregado e estará pronto para jogar.
  4. Utilize as teclas de seta para mover e rotacionar os Tetrominos:
    • Seta para esquerda: Move o Tetromino para a esquerda.
    • Seta para direita: Move o Tetromino para a direita.
    • Seta para cima: Rotaciona o Tetromino.
    • Seta para baixo: Move o Tetromino para baixo.
  5. Use os botões de controle para reiniciar, pausar e zerar a pontuação do jogo.

Mais Posts

A função WITH RECURSIVE no PostgreSQL

A função WITH RECURSIVE no PostgreSQL

A função WITH RECURSIVE no PostgreSQL é uma ferramenta poderosa para a execução de consultas recursivas em bancos de dados.... Leia mais

Guia de Deploy de Aplicação Laravel com Nginx em uma VPS com Ubuntu e HTTPS em um VPS

Guia de Deploy de Aplicação Laravel com Nginx em uma VPS com Ubuntu e HTTPS em um VPS

Neste post, você aprenderá a fazer o deploy de uma aplicação Laravel em uma VPS rodando Ubuntu com o servidor... Leia mais

Como Usar Between Entre Duas Colunas de Data no Laravel

Como Usar Between Entre Duas Colunas de Data no Laravel

Trabalhar com intervalos de datas é uma necessidade comum em aplicações web, especialmente em sistemas que lidam com eventos, agendamentos... Leia mais

Script para automatizar as instalações de extensões do php em distribuições LINUX

Script para automatizar as instalações de extensões do php em distribuições LINUX

Publiquei neste artigo https://paulorb.dev/alternar-entre-multiplas-versoes-no-php/ formas de como instalar diversas versões do PHP em distros Linux mas surgiu um problema que... Leia mais