Como fazer o seu próprio joguinho da cobrinha em Python
Vou mostrar como criar uma versão funcional do joguinho da cobrinha usando pygame. Não precisa de muito conhecimento prévio, mas é bom saber o básico de Python primeiro. O pygame instala com um simples pip install pygame.
O básico do joguinho da cobrinha
A lógica central é mais simples do que muita gente pensa. Você tem uma cobra representada por uma lista de coordenadas, uma direção, e uma maçã que aparece em posições aleatórias. A cada tick do jogo, a cobra se move na direção atual, verifica colisão, e o corpo acompanha a cabeça removendo o último segmento quando não come nada. O problema mais chato que eu encontrei trabalhando nisso foi a maçã aparecendo exatamente em cima do corpo da cobra. Parece bobeira, mas se você só sortear uma posição aleatória sem verificar, o jogo começa já quebrado ou a cobra come a si mesma no primeiro movimento. A solução que eu uso é simples: gere a posição da maçã dentro de um loop while que checa se aquela coordenada existe na lista do corpo da cobra. Enquanto existir colisão, gera de novo.
Aqui vai o código mínimo funcional. Crie um arquivo chamado snake.py:
import pygame
import random
import sys
pygame.init()
LARGURA = 600
ALTURA = 400
TAM_CELULA = 20
FPS = 10
COR_FUNDO = (30, 30, 30)
COR_COBRA = (50, 180, 80)
COR_MACA = (220, 60, 60)
COR_TEXTO = (255, 255, 255)
janela = pygame.display.set_mode((LARGURA, ALTURA))
pygame.display.set_caption("Joguinho da cobrinha")
relogio = pygame.time.Clock()
fonte = pygame.font.Font(None, 36)
def gerar_maca():
x = random.randint(0, (LARGURA - TAM_CELULA) // TAM_CELULA) * TAM_CELULA
y = random.randint(0, (ALTURA - TAM_CELULA) // TAM_CELULA) * TAM_CELULA
return (x, y)
def desenhar_maca(x, y):
pygame.draw.rect(janela, COR_MACA, (x, y, TAM_CELULA, TAM_CELULA))
def desenhar_cobra(cobra):
for segmento in cobra:
pygame.draw.rect(janela, COR_COBRA, (segmento[0], segmento[1], TAM_CELULA, TAM_CELULA))
def mostrar_pontuacao(pontos):
texto = fonte.render(f"Pontos: {pontos}", True, COR_TEXTO)
janela.blit(texto, (10, 10))
def jogo_terminou():
texto = fonte.render("FIM DE JOGO! Pressione R para recomecar", True, COR_TEXTO)
janela.blit(texto, (LARGURA // 2 - 200, ALTURA // 2))
pygame.display.update()
def main():
cobra_inicial = [(300, 200), (280, 200), (260, 200)]
cobra = cobra_inicial.copy()
direcao = (TAM_CELULA, 0)
maca = gerar_maca()
pontos = 0
game_over = False
while True:
for evento in pygame.event.get():
if evento.type == pygame.QUIT:
pygame.quit()
sys.exit()
if evento.type == pygame.KEYDOWN:
if game_over and evento.key == pygame.K_r:
main()
if not game_over:
if evento.key == pygame.K_UP and direcao != (0, TAM_CELULA):
direcao = (0, -TAM_CELULA)
elif evento.key == pygame.K_DOWN and direcao != (0, -TAM_CELULA):
direcao = (0, TAM_CELULA)
elif evento.key == pygame.K_LEFT and direcao != (TAM_CELULA, 0):
direcao = (-TAM_CELULA, 0)
elif evento.key == pygame.K_RIGHT and direcao != (-TAM_CELULA, 0):
direcao = (TAM_CELULA, 0)
if game_over:
janela.fill(COR_FUNDO)
jogo_terminou()
pygame.display.update()
relogio.tick(FPS)
continue
nova_cabeca = (cobra[0][0] + direcao[0], cobra[0][1] + direcao[1])
if (nova_cabeca[0] < 0 or nova_cabeca[0] >= LARGURA or
nova_cabeca[1] < 0 or nova_cabeca[1] >= ALTURA or
nova_cabeca in cobra):
game_over = True
janela.fill(COR_FUNDO)
jogo_terminou()
pygame.display.update()
relogio.tick(FPS)
continue
cobra.insert(0, nova_cabeca)
if nova_cabeca == maca:
pontos += 10
maca = gerar_maca()
else:
cobra.pop()
janela.fill(COR_FUNDO)
desenhar_maca(*maca)
desenhar_cobra(cobra)
mostrar_pontuacao(pontos)
pygame.display.update()
relogio.tick(FPS)
if __name__ == "__main__":
main()
Executa com python snake.py e pronto. O jogo rodando a 10 FPS fica jogável sem parecer acelerado demais. Tem um detalhe que muita gente erra na hora de expandir o projeto: o sistema de input. Se você apertar rapidamente para cima e depois para a esquerda num mesmo frame, a cobra pode virar 180 graus e colidir consigo mesma mesmo com a verificação de direção. O fix é garantir que apenas uma mudança de direção seja processada por tick. Eu resolvi com uma flag que trava novas entradas até o próximo frame de movimento. Ficou assim no meu código final:
👉 Clique no botão abaixo para saber mais sobre o assunto!
aguardando_input = False
No loop de eventos:
if not aguardando_input:
if evento.key == pygame.K_UP and direcao != (0, TAM_CELULA):
nova_direcao = (0, -TAM_CELULA)
aguardando_input = True
Depois de processar o movimento:
if aguardando_input:
direcao = nova_direcao
aguardando_input = False
Isso evita aquele bug clássico onde a cobra morre sozinha num aperto de tecla duplo. Leva dois minutos pra implementar e economiza meia hora de debugging. Se quiser fazer uma versão web, a abordagem com canvas do HTML5 funciona bem também. A lógica é basicamente a mesma, só que trocando pygame por requestAnimationFrame e manipulando o context 2D. O desempenho é similar para este tamanho de jogo. A desvantagem é que você perde o controle de FPS nativo e precisa gerenciar o loop manualmente, o que às vezes resulta em animações oscilando entre 20 e 60 FPS dependendo do browser e da carga da aba.
O arquivo zipado com o código completo e os assets se você quiser testar está disponível no meu GitHub. O link é github.com/agusilva/snake-game-python. Tem também uma branch com a versão web caso precise rodar no navegador sem instalar nada. Uma coisa que as tutoriais básicos não mencionam: o tamanho da grade influencia diretamente na dificuldade. Com celulas de 20 pixels e janela de 600x400, você tem 30x20 celulas. Reduzir para 15 pixels deixa o jogo visualmente mais poluído sem ganho real de jogabilidade. Aumentar para 30pixels deixa os movimentos muito grandes e a cobra praticamente impossível de controlar em velocidades mais altas. O sweet spot fica entre 15 e 20 pixels para janelas nessa faixa de resolução.
Outro ponto que parece óbvio mas não é: a velocidade. A maioria dos tutoriais fixa o FPS e pronto. Na prática, o jogo fica mais difícil conforme a cobra cresce porque o tempo entre frames permanece o mesmo mas a área de manobra diminui. Se quiser dar uma progressão real de dificuldade, aumente o FPS gradualmente a cada 50 pontos. Eu comecei com 10 FPS e vou subindo até 18 no máximo. Passou disso e o jogo fica insuportável na prática. Se o objetivo for mesmo só jogar e não construir, existem implementações open source maduras como o snake-py no PyPI que já vem com menus, sons e save de records. Não tem graça nenhuma começar do zero se o que você quer é só ter o joguinho da cobrinha rodando na mesa. A instalação é pip install snake-py e roda com python -m snake_py. Leva 30 segundos e funciona.
Eu costumava sugerir quem perguntava nos fóruns para começar pelo pygame, mas honestamente a curva de aprendizado vale a pena só se você quer entender como o jogo funciona por dentro. Se o interesse é só jogar, use as bibliotecas prontas e gaste o tempo em outra coisa.