Como dito em tópico anterior, foi lançado o desafio há uma semana para a criação de um jogo de Snake usando Python. A ideia era o pessoal treinar lógica e aprender a fazer o jogo antes de eu jogar a solução aqui. Mesmo que ninguém tenha mandado resposta, tou com a ideia de fazer isso agora de vez em quando pra ajudar quem tá estudando, pois é muito melhor ler um tópico de tutorial já tendo tentado fazer algo antes do que ler sem nunca ter tentado.
Decidi dividir o tópico em duas partes. Na primeira parte eu irei desenhar a tela, a cobra e a maçã no jogo e na segunda parte eu adiciono a lógica do jogo.
Então, para criar nosso jogo da cobrinha, vou usar a lib pygame. Criado um ambiente virtual, precisamos instalar pygame:
(venv) Extra 21$ pip install pygame
Agora, vou criar o script snake.py e importar o que eu vou precisar da lib:
# snake.py
import pygame
import pygame.locals as pl
Vou primeiro criar o ambiente do nosso jogo. Vou precisar de uma tela para nossa cobra andar nela.
Pensei em inicialmente fazer o programa rodar todo em funções mas ia dar um trabalhinho a mais entender o que passa como parâmetro para o que, então posso tentar fazer isso numa refatorada depois. A intenção é mais mostrar como rodar um jogo funcional então decidi que não vou em preocupar muito com estética aqui.
Criando a tela do jogo:
import pygame
import pygame.locals as pl
# Inicia a lib pygame
pygame.init()
# Adiciona um título à janela de jogo
pygame.display.set_caption('Snake')
# Cria a tela do jogo de 600px por 500px
tela = pygame.display.set_mode((600, 500))
Rodando o código acima, uma tela irá abrir e fechar rapidamente.
Todo jogo é um loop infinito, então para que minha tela não feche enquanto eu jogo, preciso adicionar um loop infinito aqui para que a tela não suma:
import pygame
import pygame.locals as pl
...
while True:
# Com o comando abaixo consigo que minha janela não feche
pygame.display.update()
Com o que temos até agora já consigo renderizar uma tela de jogo. Mas minha tela não fecha se eu clicar no botão de fechar.
Além disso, a tela do gif que eu criei era verde, e a tela que aparece até o momento é preta, então vamos mudar a cor da tela:
...
while True:
# Muda a cor da tela para verde
tela.fill((93, 205, 84))
# Mantém a tela aberta
pygame.display.update()
O que está dentro de tela.fill é um código RGB de cores. Pra quem não sabe o que é RGB:
Colors RGBPara conseguir fechar a janela ao clicar no botão de fechar, eu preciso que meu jogo escute eventos do jogador, sendo, neste caso, o evento de fechar janela ao clicar no botão apropriado. Então, vamos adicionar o código abaixo:
...
while True:
# Recebe todos os eventos do jogador na tela
for event in pygame.event.get():
# Se o evento for QUIT, ao clicar no botão de quit, fecha o programa
if event.type == pl.QUIT:
pygame.quit()
# Muda a cor da tela para verde
tela.fill((93, 205, 84))
# Mantém a tela aberta
pygame.display.update()
Agora, ao clicar no botão de fechar, nossa janela é fechada.
Se o jogo é o jogo da cobrinha, precisamos de uma cobrinha. Aqui, irei criar uma lista de tuplas com as coordenadas de cada parte de nossa cobra. Imaginem a tela como um conjunto de coordenadas x e y, ou seja, a parte horizontal da tela é o nosso eixo x e a parte vertical é o nosso eixo y, e o ponto mais à esquerda e acima é o ponto (0, 0), mas aqui, ao descer o valor de y aumenta, e ao ir para a direita, o valor de x aumenta.
Então vou criar uma cobra que vai ocupar os pontos (150, 150), (160, 150), (170, 150) e (180, 150) e cada quadrado dela vai ter o tamanho de 10 por 10:
import pygame
import pygame.locals as pl
...
# Cria a cobra
snake = [(150, 150), (160, 150), (170, 150), (180, 150)]
snake_corpo = pygame.Surface((10, 10))
while True:
# Recebe todos os eventos do jogador na tela
for event in pygame.event.get():
# Se o evento for QUIT, ao clicar no botão de quit, fecha o programa
if event.type == pl.QUIT:
pygame.quit()
# Muda a cor da tela para verde
tela.fill((93, 205, 84))
# Desenha a cobra na tela
for posicao in snake:
tela.blit(snake_corpo, posicao)
# Mantém a tela aberta
pygame.display.update()
Duas coisas importantes aqui: Eu criei a lista com as posições que a cobra vai ocupar. Mas pra desenhar a cobra na tela, eu preciso iterar sobre cada ponto desse, que é o que foi feito em "for posicao in snake". A função de tela "blit" desenha sprites na nossa tela. Aqui, pra cada pedaço da cobra, a tela desenha o pedaço nas coordenadas que eu escolhi. Além disso, "tela.fill" precisa estar antes do desenho de qualquer sprite na tela, tanto da cobra quanto da maçã, pois a cada volta no loop infinito eu preciso que a tela se desenhe antes para que eu desenhe a cobra e a maçã por cima. Se eu colocar "tela.fill" depois do desenho da cobra, a cobra nunca vai aparecer, pois a tela sempre será colocada depois do desenho da cobra, sobrepondo a cobra.
Rodando o jogo agora o que aparece na nossa tela é o seguinte:

E a cobra come maçãs. Pra desenhar uma maçã na tela o princípio é o mesmo. Mas a maçã não precisa ser uma lista de coordenadas. A maçã é um único ponto na nossa tela:
import pygame
import pygame.locals as pl
...
# Cria a maçã, de tamanho 10 por 10, de cor vermelha e na posição 300 x 300 da tela
apple = pygame.Surface((10, 10))
apple.fill((255, 0, 0))
apple_posicao = (300, 300)
while True:
...
# Muda a cor da tela para verde
tela.fill((93, 205, 84))
# Desenha a maçã na tela na posição selecionada
tela.blit(apple, apple_posicao)
for posicao in snake:
tela.blit(snake_corpo, posicao)
# Mantém a tela aberta
pygame.display.update()
Rodando agora:

Podemos dizer que a primeira parte do desafio era esta. Criar a tela, a cobra e a maçã. A lógica do jogo fica pra parte 2.