POTHIEDU Não Decore a Resposta. Entenda o Caminho

Jogo Da Minhoca Que Come Frutas

Jogo Da Minhoca Que Come Frutas

Como fazer um jogo da minhoca que come frutas

O conceito é simples, mas a implementação decente tem algumas pegadinhas que ninguém menciona nos tutoriais básicos. Vou explicar do jeito que funciona na prática.

O clássico jogo da minhoca que come frutas

Você precisa de um canvas HTML5, um loop de jogo rodando a 10-15fps, e uma grade onde a cobra e as frutas vivem. O código base em JavaScript é curto, mas o que separa um joguinho que trava depois de cinco minutos de algo que roda liso é o gerenciamento de estado. Aqui vai o esqueleto funcional:

const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); const gridSize = 20; const tileCount = 25;

O loop principal usa setInterval ou requestAnimationFrame com throttle. Eu recomendo setInterval com 100ms para manter o controle preciso da velocidade da cobra sem depender do refresh rate do monitor do usuário. A cobra é um array de coordenadas. Cada frame, você adiciona uma nova cabeça na direção atual e remove a cauda. Se a cabeça colidir com a fruta, não remove a cauda — isso faz a cobra crescer.
Problema real que encontrei: se você gerar a fruta em posição totalmente aleatória, existe chance dela spawnar em cima do corpo da cobra. Isso quebra o jogo. A solução que uso é um loop while que verifica colisão com todos os segmentos do corpo antes de confirmar a posição.

Performance e edge cases

Num jogo simples como esse, performance não é problema até você adicionar efeitos visuais ou telas múltiplas. O problema real é input lag. Se o jogador apertar duas teclas rápido demais entre frames, a cobra pode virar 180 graus e colidir consigo mesma instantaneamente. Sempre bloqueie inputs opostos — se a cobra vai para direita, ignore comando de esquerda até o próximo frame. Outro detalhe que poucos mencionam: use requestAnimationFrame apenas para renderização e setInterval separado para lógica do jogo. Isso evita que a velocidade da cobra dependa da taxa de quadros do GPU. Num monitor de 144Hz, seu jogo rodaria duas vezes mais rápido se usar apenas requestAnimationFrame.

Implementação completa

Aqui está uma versão que funciona e cobre os casos que eu citei:

let snake = [{x: 10, y: 10}]; let food = {x: 15, y: 15}; let dx = 1; let dy = 0; let nextDx = 1; let nextDy = 0; function generateFood() { let newFood; do { newFood = { x: Math.floor(Math.random() * tileCount), y: Math.floor(Math.random() * tileCount) }; } while (snake.some(seg => seg.x === newFood.x && seg.y === newFood.y)); food = newFood; } function gameLoop() { dx = nextDx; dy = nextDy; const head = {x: snake[0].x + dx, y: snake[0].y + dy}; if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) { resetGame(); return; } if (head.x === food.x && head.y === food.y) { generateFood(); } else { snake.pop(); } snake.unshift(head); draw(); } function draw() { ctx.fillStyle = '#111'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#4CAF50'; snake.forEach(seg => ctx.fillRect(seg.x * gridSize, seg.y * gridSize, gridSize - 2, gridSize - 2)); ctx.fillStyle = '#FF5722'; ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize - 2, gridSize - 2); } document.addEventListener('keydown', e => { if (e.key === 'ArrowUp' && dy !== 1) { nextDx = 0; nextDy = -1; } if (e.key === 'ArrowDown' && dy !== -1) { nextDx = 0; nextDy = 1; } if (e.key === 'ArrowLeft' && dx !== 1) { nextDx = -1; nextDy = 0; } if (e.key === 'ArrowRight' && dx !== -1) { nextDx = 1; nextDy = 0; } }); setInterval(gameLoop, 100);

Limitações que você precisa saber

Esse código funciona para um jogo simples de teste ou projeto escolar. Ele tem problemas sérios se você quiser algo profissional. Não há gestão de memória para arrays que crescem indefinidamente — numa partida longa, a cobra pode ocupar toda a grade e o jogo travar. A renderização redesenha tudo a cada frame, o que é aceitável para poucos elementos mas escala mal. Se o objetivo é publicar algo em uma loja de jogos ou app store, considere frameworks como Phaser ou Godot. Eles resolvem questões de input buffering, colisão precisa, e save states que esse código básico não oferece. O tempo gasto refinando o código vanilla versus configurar um engine varia bastante — num projeto simples como esse, vanilla sai mais rápido. Num projeto maior, engine compensa em semanas de desenvolvimento.