Como criar um jogo usando Python

Requisitos

Para seguir esse tutorial, você vai precisar ter o Python instalado (preferencialmente a versão 3.x) e a biblioteca PyGame, além de um editor de textos de sua preferência (para esse tutorial, usei a IDE Spyder). Você pode encontrar os tutoriais de instalação e de sintaxe abaixo:
  • Hello World em Python: caso você nunca tenha visto nada de Python, recomendo começar olhando esse post que explica o básico da linguagem
  • Sintaxe Básica do Python: aqui você poderá encontrar uma explicação sobre a sintaxe básica e as principais bibliotecas e funcionalidades da linguagem Python.
  • Biblioteca PyGame: aqui você encontrará como instalar a biblioteca e uma explicação sobre todos os comandos usados nesse tutorial.


Repositório do jogo

Caso queira consultar o código do jogo, ele está disponível em: https://github.com/LucasFerrazSilva/snake-game


Definindo o jogo

O primeiro passo é definirmos qual jogo iremos implementar (seu layout, sua mecânica, etc.). Para esse tutorial, optei por recriar o famoso "jogo da cobrinha", ou Snake.

As regras são bem conhecidas, mas vamos recapitular:

  • A cobra começa com 3 blocos de comprimento.
  • Sempre que ela "comer" uma fruta, ela cresce um bloco e o jogador ganha um ponto.
  • Ela tem velocidade constante.
  • Se ela encostar em uma parede, ou no próprio corpo, o jogo termina.
  • O jogador vence caso não tenha mais espaço para a cobra crescer (boa sorte com isso).
  • A cobra só pode girar 90 º por movimento.
  • As frutas são geradas em pontos aleatórios da tela.
  • A fruta não pode ser gerada em um ponto que já esteja ocupado pela cobra.

O layout que implementaremos será o seguinte:


Além disso, iremos criar uma tela de game-over informando o score atingido. Também existirá uma mensagem informando que o usuário pode jogar novamente apertando a tecla "Espaço" ou sair do jogo apertando a tecla "Esc".


Estrutura de um jogo

Um ponto que tem que ficar claro antes de programarmos qualquer coisa, é como é uma estrutura padrão de um jogo:

  • inicialização de variáveis / criação dos objetos;
  • laço infinito até que usuário escolha por sair do jogo;
    • verificação de interações do usuário;
      • teclas pressionadas;
      • movimentação do mouse;
      • cliques do mouse;
      • etc.
    • reset da tela (para a cor de fundo padrão);
    • lógica do jogo de acordo com interação (ou falta de) do usuário;
      • movimentação de objetos;
      • verificação de regras como incremento dos pontos ou fim de jogo;
      • etc.
    • objetos são desenhados na tela conforme lógica executada.

Agora que sabemos qual a estrutura básica nosso código deve seguir, vamos definir algumas propriedades.


Criando uma pasta para o projeto

É importante que você crie uma pasta para esse projeto, pois iremos criar vários arquivos que tem que ficar localizados no mesmo local.


Criando a classe Position

Como iremos trabalhar, a todo momento, com a posição dos objetos, iremos criar uma classe que irá encapsular os atributos x e y das posições e alguns métodos úteis, como verificar se uma posição é igual à outra. Para isso, crie um arquivo chamado position.py.

O arquivo deve conter o seguinte:




Definindo as propriedades

Para termos um local único para definição de propriedades e termos fácil acesso à elas, iremos criar um arquivo properties.py, que irá conter:

  • Tamanho da área jogável: iremos definir que a área jogável será um quadrado de 600x600.
  • O valor da margem padrão (que será diferente da margem do score): 20.
  • O valor da margem do score: 80.
  • Tamanho total da janela: cálculo considerando o tamanho da área jogável, margem padrão e margem do score.
  • Coordenada (x, y) inicial da área jogável: como essa coordenada será usada em diversos locais, faz sentido que esse cálculo seja feita apenas uma vez.
  • Coordenada (x, y) final da área jogável: também será bastante usada no código.
  • Tamanho dos blocos: os blocos serão a menor unidade de tamanho do jogo. Essa informação será usada para gerar os blocos no tamanho e locais corretos, além de garantir que a cobra tenha um tamanho e movimento correto.
Portanto, nosso arquivo properties.py ficará desse jeito:



Cores

Vamos criar um arquivo que irá conter o valor RGB para as cores que iremos utilizar no jogo. Isso serve para facilitar a escrita e leitura do código.

Crie um arquivo colors.py:





Score

Vamos criar uma classe que irá representar o score do usuário. Ela precisa:

  • Guardar o score conforme o usuário joga.

  • Desenhar na tela a palavra "Score" e, embaixo, a pontuação do usuário.

Portanto, crie um arquivo score.py e, dentro dele, crie a classe Score:



Grid

Agora vamos criar uma classe que irá simplesmente desenhar as linhas da parte jogável da tela para que o usuário saiba até onde ele pode ir.

Cri um arquivo grid.py com o seguinte conteúdo:



Snake

Vamos criar a classe mais importante: a que representa a cobra. Porém, antes disso, precisamos criar uma classe para representar as partes da cobra, visto que cada parte dela tem uma posição e direção de movimento diferentes.

Primeiro, crie um arquivo move_direction.py, que será uma ENUM que irá representar os possíveis movimentos. 

Também iremos usar essa enum para garantir que os movimentos sejam de 90º, usando a seguinte lógica: o movimento é inválido caso a sobra da divisão do (valor do próximo movimento menos o valor do movimento atual) por dois seja 0 (move_direction.value - actual_move_direction.value % 2 == 0).

Veja como funciona:

Exemplo 1:

    Movimento atual: Left (2)

    Próximo movimento: Up (1)

    (1-2) = -1

    -1 % 2 = -1

    Portanto, Ir de Left para Up é válido.

Exemplo 2:

    Movimento atual: Right(4)

    Próximo movimento: Left (2)

    (2 - 4) = -2

    -2 % 2 = 0

    Portanto, Ir de Right para Left é inválido.

Enfim, crie o arquivo move_direction.py:


A seguir, crie o arquivo snake_piece.py:

Note que ele tem um método copy(). Usaremos ele para copiar a última parte da cobra quando uma fruta for consumida pela cobra e ela tiver que aumentar de tamanho.

Por fim, vamos criar a classe Snake. Por ser a classe mais importante do jogo, ela é também a mais complexa, então vamos quebrar ela em partes para facilitar a explicação.

Crie um arquivo snake.py e adicione os seguintes imports e o método __init__:

                                            

No método __init__(), definimos a cor da cobra como verde, inicializamos a lista que irá conter suas partes, chamamos o método que gera o corpo inicial da cobra (com 3 partes) e definimos a velocidade. É importante definir a velocidade porque quando o usuário pressionar nenhuma tecla de direção, ela deverá se mover constantemente na direção definida.

O método __generate_initial_body() primeiro chama o método __init_head_snake_piece(), que irá encontrar o bloco central da área jogável e criar a primeira parte da cobra ali. Em seguida, partindo dessa posição, são criados mais duas partes à direita.

O método __init_speed() simplesmente define a velocidade como 7 (fique à vontade para alterar esse valor para aumentar ou diminuir a dificuldade do jogo)


Com isso, já conseguimos criar o método para desenhar a cobra na tela:



Para movermos a cobra, precisamos criar o método move, que deve verificar algumas coisas:

  • Caso o usuário tenha pressionado uma tecla de direção, ele deve ignorar caso a tecla seja inválida (mesma direção ou direção oposta da atual)
  • Caso o usuário não tenha pressionado uma tecla, deve verificar se já é hora de mover a cobra
  • Caso seja necessário mover a cobra, deve controlar a lógica de acordo com a direção
  • Deve garantir que as partes mudem de direção conforme as demais



Os seguintes métodos verificam se a cobra comeu a fruta (verificando as posições) e, caso positivo, incrementa a cobra:



Por fim, precisamos criar os métodos pra verificar se a cobra colidiu com a parede ou com ela mesma:



Fruta

Agora vamos criar a classe que irá representar a fruta na tela. Ela será responsável por:

  • Desenhar a fruta na tela.
  • Criar a fruta em uma posição válida.

Crie um arquivo food.py:



Game Over

Agora vamos criar a classe que irá mostrar a:

  • Mensagem de Game-Over.
  • Pontuação do jogador.
  • Mensagem perguntando se ele deseja jogar novamente.

Crie um arquivo game_over.py:





Game Manager

Por fim, vamos criar a classe que irá controlar esses objetos e de fato fazer o jogo rodar.

Por ser a classe que irá controlar todos os objetos, ela também é relativamente grande, então vamos quebrar a criação dela em partes.

Crie a classe game_manager.py e importe as dependências necessárias:


 Em seguida, vamos criar os métodos de inicialização das variáveis e objetos e o método de finalização do jogo:



Agora vamos adicionar a estrutura básica, mencionada no início do post:



Vamos adicionar o tratamento de eventos (interações do usuário):



Para finalizar, vamos adicionar a lógica do jogo e o desenho dos objetos:


Pronto!


Testando o jogo

Se você seguiu corretamente todos os passos descritos, o seu resultado programa deve estar executando conforme abaixo:



Conclusão

Espero que tenha gostado do pequeno projeto. Apesar de ser um jogo simples, podemos ver que programá-lo não é tão simples como parece, mas é bastante divertido. 


Links Úteis



Comentários

Postagens mais visitadas deste blog

Biblioteca Python: Random