Classes no Pygame
Classes dos componentes visuais
Vamos começar com um exemplo de botões, semelhante ao que fizemos no handout sobre colisões. Suponha que queremos ter tanto botões circulares quanto retangulares. Vamos listar algumas características de qualquer botão:
- Deve ser desenhado na tela;
- Quando ocorre um clique dentro de sua área, ele reage de alguma forma.
Para simplificar, vamos supor que quando o botão é clicado ele muda de cor.
Exercício 1
Note que adotar classes muda muito o código do nosso jogo. Uma das vantagens é tirar código das funções principais do jogo e agrupar tudo relativo a um tipo de elemento do jogo em uma classe. Por exemplo, ao invés de termos vários dicionários representando botões no dicionário state
agora podemos ter somente uma lista de objetos do tipo Botao
. Depois disso, no código principal, não precisamos mais nos preocupar com a implementação específica da colisão do mouse com a forma (círculo ou retângulo). Basta chamar a função verifica_clique
.
Mais um exemplo#
Vamos introduzir um recurso disponível no Pygame através de um exemplo. O código que gera o "jogo" abaixo já está pronto no exercício Movimentos aleatórios. Nós vamos modificá-lo ao longo deste handout.
Exercício 2
Vamos começar entendendo o código que já foi fornecido.
Exercício 3
Resposta
O construtor guarda em atributos as dimensões da janela e a lista de monstros recebidas como argumentos. Além disso, ela carrega a imagem do monstro e guarda em atributos tanto a imagem quanto o seu retângulo (que indica a sua posição). O construtor também sorteia velocidades e posições aleatórias. Caso a posição já esteja ocupada por outro monstro, continua sorteando outras posições.
Exercício 4
Resposta
O método percorre a lista de monstros e verifica se o retângulo de algum deles colide com o próprio retângulo. Esse método verifica se o monstro que está sendo testado é diferente dele mesmo, pois ele também estará na lista de monstros.
Exercício 5
Resposta
O método tenta movimentar o monstro na horizontal. Se ele sair da janela ou colidir com outro monstro, inverte a direção da velocidade. Posteriormente, o método repete esse processo para o movimento vertical.
Sprites#
Como o Pygame é uma biblioteca de desenvolvimento de jogos, é esperado que seja muito comum termos classes similares à nossa classe Monstro
: guardam uma imagem e sua posição e podem ser atualizadas para se mover no mapa ou realizar outras ações. Para facilitar o nosso trabalho, o Pygame já disponibiliza a classe pygame.sprite.Sprite
.
Exercício 6
Resposta
Devemos criar os atributos image
e rect
para definir a imagem a ser mostrada e a sua posição.
Agora precisamos introduzir um outro conceito de classes que ainda não havíamos mencionado: a herança. Uma classe pode estender as funcionalidades de outra classe através do mecanismo de herança. No exemplo da documentação, a classe Block
estende a classe pygame.sprite.Sprite
com a declaração da primeira linha:
Isso quer dizer que o Block
é um pygame.sprite.Sprite
, ou seja, o Block
tem e faz tudo o que o pygame.sprite.Sprite
faz e mais algumas coisas.
No construtor da classe Block
, existe também a seguinte chamada:
Essa linha chama a inicialização do pygame.sprite.Sprite
. Como o Block
é um pygame.sprite.Sprite
, precisamos inicializar a parte pygame.sprite.Sprite
dele.
Exercício 7
Sprite Groups#
A classe pygame.sprite.Sprite
pode ser usada em conjunto com a classe pygame.sprite.Group
. O pygame.sprite.Group
é similar a uma lista, mas com algumas funcionalidades extras. Ele já possui métodos que:
- Desenham todos os seus sprites;
- Chamam o método
update
de todos os seus sprites (por isso renomeamos oatualiza
paraupdate
no exercício anterior).
Além disso, existem funções que verificam colisões entre sprites e grupos ou entre grupos de sprites.
Exercício 8
Exercício 9
Resposta
Os argumentos são:
sprite
: um objeto do tipopygame.sprite.Sprite
que será o alvo da colisão com todos os sprites do grupo;group
: um grupo de sprites do tipopygame.sprite.Group
. A função verifica quais sprites deste grupo colidiram com o primeiro argumento;dokill
: remove todos os sprites do grupo que colidirem com o sprite do primeiro argumento.
O valor retornado pela função é uma lista com todos os sprites do grupo que colidiram com o sprite principal (primeiro argumento).
Exercício 10
Melhorando a colisão#
Talvez você tenha reparado que as colisões são um pouco estranhas. Os monstros "colidem" muito antes de realmente encostarem. Isso acontece porque estamos comparando os retângulos de suas imagens. Essas imagens são transparentes, então o desenho do monstro em si ocupa apenas uma parte central da imagem.
Vamos aproveitar que os monstros são relativamente circulares e vamos trocar a colisão por algo uma um pouco mais precisa. Vamos utilizar a colisão de círculos, que o Pygame também já tem implementada. Na verdade, é para isso que serve o último argumento da função pygame.sprite.spritecollide
.
Exercício 11
Resposta
Caso o sprite tenha o atributo radius
, ele será utilizado para a colisão entre sprites, quando a função de colisão de círculos for utilizada.
Exercício 12
Outros métodos de sprites#
O módulo pygame.sprite
possui diversas outras classes e métodos que podem ser úteis em algum momento. É sempre bom dar uma olhada geral na documentação para saber o que existe, caso você venha a precisar. Você não precisa (e nem deve) se preocupar em conhecer toda a documentação e decorar todos os métodos e classes. Isso vem com o uso, mas é importante ter dado uma olhada geral alguma vez, para saber que existe algo pronto que já resolve o seu problema, quando ele surgir.
Em particular, as funções a seguir podem ser úteis em algum momento do seu projeto:
Agora que vimos algumas classes do Pygame, vamos também refletir sobre o uso de classes em outros lugares do nosso jogo.