O jogo da cobra azul que todo mundo tenta implementar e quase sempre estraga
Eu já vi dezenas de projetos de jogo da cobra azul rodando em sala de aula e em repositórios no GitHub. A maioria tem o mesmo problema: alguém copia um tutorial básico, coloca um grid 20x20, faz a cobra crescer quando come a maçã e chama de completo. Funciona. Até você tentar adicionar colisão com as próprias paredes do canvas ou sincronizar o loop com requestAnimationFrame sem considerar o framerate da máquina do usuário. O jogo da cobra azul propriamente dito é simples na teoria. Você tem uma cobra representada por uma lista de coordenadas, um objeto comida posicionado aleatoriamente, e um loop que atualiza a posição a cada X milissegundos. O input vem de um event listener de teclado. Mas é exatamente nessa simplicidade que mora a armadilha.
Implementação prática do jogo da cobra azul
Para começar, esqueça o setTimeout como motor principal do loop. Ele é imprevisível em navegadores modernos porque o browser pode throttlar tabs inativas. Use requestAnimationFrame com cálculo de delta time. Desta forma, a cobra se move na taxa que você definir, independentemente se a máquina roda a 60fps ou a 144fps. Aqui está a estrutura básica que eu uso:
Um array de segmentos, onde cada elemento é um objeto {x, y}. A cobra cresce quando a cabeça colide com a comida. A direção é armazenada como um vetor {dx, dy} e atualizada apenas nos eventoskeydown. O importante é não permitir viradas de 180 graus — se a cobra vai para a direita, o jogador não pode pressionar imediatamente para a esquerda. Eu resolvi isso mantendo um flag lastDirection que só é liberado após cada atualização do frame.
O bug que eu levei três horas para descobrir
Em um projeto específico de jogo da cobra azul, a cobra frequentemente atravessava paredes e continuava crescendo. O comportamento era intermitente, aparecia apenas em resoluções acima de 1920px. Descobri que o problema era o escalaamento do canvas. Eu estava desenhando em coordenadas lógicas mas colidindo com coordenadas físicas do DOM. A solução foi separar completamente a camada de renderização da camada de lógica, usando uma constante GRID_SCALE para converter entre os dois espaços.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Pitfalls comuns que iniciantes ignoram
O primeiro é a geração de comida. Posicionar aleatoriamente sem verificar se o espaço já está ocupado por um segmento da cobra gera colisões instantly. Eu criei uma função que mantém um Set das células ocupadas e faz reject- sampling até encontrar um slot livre. Em tabuleiros cheios, isso pode exigir até 40 tentativas, mas na prática raramente passa de 3. O segundo é o movimento. Muitos tutoriais movem a cobra removendo o último segmento e adicionando uma nova cabeça. Isso funciona, mas esquecem de tratar o caso onde a cobra mora ao tocar em si mesma. A verificação de colisão deve acontecer ANTES da atualização do frame, não depois. Do contrário, você vê a cobra atravessando seu próprio corpo e o jogo continua.
Limitações que ninguém comenta
O jogo da cobra azul tradicional tem um limite prático de usabilidade: quanto maior a cobra, mais lento o jogo precisa ser para permanecer jogável. Em grids maiores que 30x30, a velocidade percebida cai drasticamente porque o cérebro humano leva mais tempo para processar trajetórias longas. Minha recomendação é manter o grid entre 15x15 e 25x25, com velocidade inicial de 150ms por frame e decremento de 2ms a cada fruta coletada, com floor mínimo de 60ms. Alternativamente, você pode implementar um modo sandbox onde a cobra não morre ao colidir consigo mesma, focando apenas em pontuação e velocidade máxima. Isso transforma o jogo de um desafio de sobrevivência para um desafio de performance pura.
Código funcional mínimo
Para quem quer rodar algo que funcione na primeira tentativa, aqui está a essência. Um canvas de 400x400, grid de 20x20, cobra inicial de 3 segmentos na posição central, comida gerada fora do corpo, e loop com requestAnimationFrame. O keydown listener usa e.preventDefault() apenas para as setas e WASD, senão a página role quando o jogador tenta mudar de direção. Isso é sutil mas irritante se não for tratado.
Conclusão prática
O jogo da cobra azul é um exercício clássico porque cobre fundamentos: estado, loop de jogo, input, colisão e renderização. Mas a qualidade da implementação separa os projetos que funcionam dos que parecem profissionais. Trate a separação lógica/renderização como sagrada, valide cada colisão antes de aplicar, e nunca confie em setTimeout para timing crítico. Se você estiver construindo isso para portfólio, adicione um sistema de high score salvo em localStorage com timestamp. É trivial e demonstra atenção a detalhes que recrutadores percebem.