Foco da Matéria: Criar os obstáculos (tubos) dinamicamente com JavaScript, movê-los e, o mais importante, implementar a lógica de detecção de colisão entre o pássaro e os tubos.

1. 🏗️ Criação Dinâmica dos Tubos

Os tubos não estão no HTML, pois eles são criados e destruídos continuamente. Usaremos JavaScript para criar os elementos DOM (div com a classe .pipe) e injetá-los no contêiner do jogo.

Vamos adicionar o código de criação e posicionamento dos tubos ao seu game.js.

JavaScript

// VARIÁVEIS DE CONFIGURAÇÃO (Já definidas no Tema 3)
// const VELOCIDADE_TUBO = 2; 
// const INTERVALO_TUBO = 1500; 

// VARIÁVEIS DE ESTADO
let tubos = []; // Array para armazenar todos os tubos ativos

// --- FUNÇÃO PARA CRIAR UM PAR DE TUBOS ---
function criarTubo() {
    if (!isGameRunning) return;

    // 1. Calcular Alturas e Espaço (Gap):
    const minHeight = 50; // Altura mínima do tubo
    const gapHeight = 120; // Espaço para o pássaro passar
    const maxTopPipeHeight = containerHeight - groundHeight - gapHeight - minHeight;

    // Altura aleatória para o tubo superior
    const topPipeHeight = Math.floor(Math.random() * maxTopPipeHeight) + minHeight;
    // Altura do tubo inferior é o restante do espaço disponível
    const bottomPipeHeight = containerHeight - groundHeight - topPipeHeight - gapHeight;

    // 2. Criar Elementos DOM para o Tubo Superior:
    const topPipe = document.createElement('div');
    topPipe.classList.add('pipe', 'top');
    topPipe.style.height = topPipeHeight + 'px';
    topPipe.style.right = '0px'; // Inicia na borda direita
    
    // Adiciona a ponta do tubo (pipe-cap)
    const topCap = document.createElement('div');
    topCap.classList.add('pipe-cap');
    topPipe.appendChild(topCap);
    
    // 3. Criar Elementos DOM para o Tubo Inferior:
    const bottomPipe = document.createElement('div');
    bottomPipe.classList.add('pipe', 'bottom');
    bottomPipe.style.height = bottomPipeHeight + 'px';
    bottomPipe.style.right = '0px'; 
    bottomPipe.style.bottom = groundHeight + 'px'; // Posiciona acima do chão
    
    // Adiciona a ponta do tubo
    const bottomCap = document.createElement('div');
    bottomCap.classList.add('pipe-cap');
    bottomPipe.appendChild(bottomCap);

    // 4. Adicionar ao Jogo e ao Array:
    gameContainer.appendChild(topPipe);
    gameContainer.appendChild(bottomPipe);

    // Armazena a referência para movimentação e colisão
    tubos.push({ top: topPipe, bottom: bottomPipe, passed: false });
}

// Chamar a função de criação a cada INTERVALO_TUBO (no startGame)
let pipeInterval;

function startGame() {
    // ... (Código do Tema 3) ...
    
    // Limpa tubos antigos se houver
    tubos.forEach(p => {
        p.top.remove();
        p.bottom.remove();
    });
    tubos = [];
    clearInterval(pipeInterval); // Limpa qualquer intervalo anterior

    // Inicia a criação periódica dos tubos
    criarTubo(); // Cria o primeiro imediatamente
    pipeInterval = setInterval(criarTubo, INTERVALO_TUBO);
    
    // ... (restante do código do Tema 3) ...
}

// Chame startGame() ao final do seu game.js para garantir que inicie.

2. 🏃 Movimento dos Tubos (Dentro do gameLoop)

O movimento dos tubos ocorre dentro do gameLoop() (da lição anterior). Eles se movem da direita para a esquerda.

Atualize a função gameLoop em game.js:

JavaScript

// --- O MOTOR DO JOGO (GAME LOOP) ---
function gameLoop() {
    // ... (código da gravidade e atualização da posição do pássaro) ...

    // 3. MOVER TUBOS E CHECAR COLISÃO:
    const pipesToRemove = [];
    tubos.forEach(pipe => {
        // Posição X atual do tubo (lida do CSS 'right')
        let pipeX = parseFloat(pipe.top.style.right) || 0;
        
        // Move o tubo para a esquerda
        pipeX += VELOCIDADE_TUBO; 
        
        // Aplica a nova posição (move na tela)
        pipe.top.style.right = pipeX + 'px';
        pipe.bottom.style.right = pipeX + 'px';

        // LÓGICA DE COLISÃO (Próximo Subtópico)

        // Verificação de Remoção: Se o tubo saiu da tela (passou da largura do container)
        if (pipeX > gameContainer.offsetWidth) {
            pipesToRemove.push(pipe);
        }

        // Lógica de Pontuação: Se o tubo passou do pássaro e ainda não foi contado
        const birdLeft = geekBird.offsetLeft;
        const pipeRightEdge = gameContainer.offsetWidth - pipeX; // Posição X da borda direita do tubo

        if (pipeRightEdge < birdLeft && !pipe.passed) {
            score++;
            scoreDisplay.textContent = `Pontuação: ${score}`;
            pipe.passed = true;
        }
    });
    
    // Limpa tubos que saíram da tela e os remove do array
    pipesToRemove.forEach(pipe => {
        pipe.top.remove();
        pipe.bottom.remove();
        tubos = tubos.filter(p => p !== pipe);
    });

    // ... (restante do código: Checar Colisão Chão/Teto e requestAnimationFrame) ...
}

3. 💥 Detecção de Colisão (O Coração do Jogo)

A colisão é verificada usando o princípio da “caixa delimitadora” (Bounding Box). O código checa se o pássaro está sobrepondo (fisicamente tocando) a área de um tubo.

Adicione a lógica de colisão DENTRO do tubos.forEach(...) no gameLoop:

JavaScript

// DENTRO DO tubos.forEach(pipe => { ... })

// 1. Coordenadas do Pássaro:
const birdRect = geekBird.getBoundingClientRect();

// 2. Coordenadas dos Tubos:
const topPipeRect = pipe.top.getBoundingClientRect();
const bottomPipeRect = pipe.bottom.getBoundingClientRect();

// 3. Verificação de Colisão (Bounding Box Algorithm):
// Condição de colisão: Verifica se o pássaro está tocando o tubo superior OU o tubo inferior
if (
    // Colisão com o tubo superior
    (birdRect.left < topPipeRect.right &&
     birdRect.right > topPipeRect.left &&
     birdRect.top < topPipeRect.bottom) ||
    
    // Colisão com o tubo inferior
    (birdRect.left < bottomPipeRect.right &&
     birdRect.right > bottomPipeRect.left &&
     birdRect.bottom > bottomPipeRect.top)
) {
    // COLISÃO DETECTADA!
    gameOver(); 
}

// ... (restante do código de movimento do tubo) ...

(Nota: getBoundingClientRect() é um método do DOM que retorna as coordenadas absolutas e dimensões de um elemento na tela, simplificando a matemática de colisão.)

Com este código, o jogo agora é completamente funcional: o pássaro se move, os tubos aparecem e, se você bater, o jogo acaba!