Introdução a Python - Parte 2

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar

Pygame é um conjunto de módulos Python projetados para a escrita de jogos 2D. Pygame adiciona funcionalidade sobre a excelente biblioteca SDL. Isso possibilita que se criem jogos repletos de recursos e programas multimidia com a linguagem Python. Pygame é altamento portável e roda em praticamente todas as plataformas e sistemas operacionais. Pygame já foi transferido milhões de vezes, e sua página oficial já recebeu milhões de acessos. (traduzido do parágrafo inicial encontrado na página oficial)

Referências

Como instalar

Em distribuições Ubuntu, a instalação de Pygame para Python3 deve ser feita com esta sequência de comandos:

sudo apt-get update
sudo apt-get install mercurial python3-dev python3-setuptools python3-numpy python3-opengl \
    libav-tools libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-ttf2.0-dev libsmpeg-dev \
    libsdl1.2-dev libportmidi-dev libswscale-dev libavformat-dev libavcodec-dev \
    libtiff5-dev libx11-6 libx11-dev fluid-soundfont-gm libfreetype6-dev \
    xfonts-base xfonts-100dpi xfonts-75dpi xfonts-cyrillic fontconfig fonts-freefont-ttf
 
sudo apt-get install python3-pip
sudo apt-get build-dep python-pygame
sudo pip3 install hg+http://bitbucket.org/pygame/pygame

Um primeiro exemplo

Este primeiro exemplo mostra como se pode criar um pequeno programa com uma imagem em movimento. Muitos elementos de pygame já aparecem nesta simples demonstração ...

#!/usr/bin/python3

import sys
import pygame

# inicia as estruturas internas do pygame
pygame.init()

# algumas variáveis necessárias:
# size: tamanho da janela
# speed: velocidade da bola
# black: tupla que representa cor preta

size = width, height = 320, 240
speed = [2, 2]
black = (0, 0, 0)

# cria a tela da janela, dimensionando de acordo com a variável "size"
screen = pygame.display.set_mode(size)

# carrega a imagem da bola
ball = pygame.image.load("ball.gif")

# obtém o retângulo que contém as dimensões da imagem da bola
ballrect = ball.get_rect()

# laço que nunca termina ...
while True:
    # trata quaisquer eventos pendentes do pygame
    for event in pygame.event.get():
        # apenas evento QUIT é considerado: ele faz com que a
        # aplicação termine. Demais eventos são ignorados
        if event.type == pygame.QUIT: sys.exit()

    # move a bola de acordo com "speed". Isso muda as coordenadas do retângulo 
    # que a contém
    ballrect = ballrect.move(speed)

    # se limites horizontais de movimentação foram atingidos, então velocidade horizontal
    # é invertida
    if ballrect.left < 0 or ballrect.right > width:
        speed[0] = -speed[0]
    # se limites verticais de movimentação foram atingidos, então velocidade vertical
    # é invertida
    if ballrect.top < 0 or ballrect.bottom > height:
        speed[1] = -speed[1]

    # pinta toda a tela de preto
    screen.fill(black)

    # copia a imagem da bola para a região definida por "ballrect"
    screen.blit(ball, ballrect)

    # mostra a imagem que foi copiada
    pygame.display.flip()

Baixe os arquivos deste exemplo para experimentá-lo. O programa está no script ball.py.


Algumas observações:

  • Antes de mais nada, o módulo pygame deve ser importado e inicializado (linhas 4 e 7)
  • A bola provavelmente se movimenta muito rápido na tela ... reduza sua velocidade para melhor visualizá-la.

Elementos da API pygame


A API Pygame dispõe de um conjunto de elementos que podem ser usados em aplicativos gráficos. Basicamente eles se dividem em:

  • Representação da tela ou de um elemento gráfico (ex: uma imagem, como a da bola)
  • Eventos, tais como os do mouse, teclado e temporizadores
  • Cores e fontes
  • Funções para desenho
  • Imagens e Sprites
  • Temporização
  • Mouse e teclado
  • ... e alguns outros

Nesta introdução a Python são apresentados alguns desses elementos, de forma que se possam implementar o jogo proposto ao final da oficina (ou algum outro que se ache mais interessante !).

Display

O display em Pygame representa a tela ou uma janela. Um display é iniciado da seguinte forma:

resolucao = (largura, altura)
janela = pygame.display.set_mode(resolucao)

A resolução é uma tupla que especifica as dimensões da janela. Se a resolução for (0,0), então a janela terá a dimensão da tela (modo tela cheia). Há outros dois parâmetros opcionais, que podem ser usados para escolher o tipo exato de display. Maiores detalhes podem ser lidos na referência sobre a inicialização do display.

Uma vez iniciado, um display pode ser usado como uma superfície. No exemplo, essa superfície é referenciada pela variável janela. O que se pode fazer cm uma superfície, que é um elemento fundamental de pygame para representar imagens, é apresentado na próxima seção. Por enquanto, é importante saber que, sempre que se fizerem modificações na tela, deve-se executar o seguinte:

pygame.display.flip()

Todas as modificações que se fazem na tela não são apresentadas imediatamente. A tela é composta por dois buffers, sendo um o buffer cujo conteúdo é apresentado, e outro onde se fazem atualizações. O método flip troca os buffers, invertendo seus papéis. Com isso, modificações na tela são acumuladas e ao final podem ser apresentadas de uma vez só.

Surface (superfície)

Um superfície representa uma imagem, possuindo uma resolução fixa.. Qualquer elemento em pygame do tipo imagem, seja carregada de um arquivo ou gerada dinamicamente, é uma superfície. Operações típicas que podem ser feitas em superfícies são cópia de outras imagens, desenho, preenchimento com cores, deslocamento, entre outras.

Uma superfície possui coordenadas corrspondentes a suas dimensões, as quais são representadas por um retângulo. A classe Rect possui a fucionalidade para representar e manipular esse retângulo. O retângulo de uma superfície pode ser obtido pelo método get_rect, como se pode ver na linha 25 do exemplo da bola:

ballrect = ball.get_rect()

O retângulo possui os atributos right,left,top,bottom, para os limites de cada lado. Se esses atributos forem modificados, pode-se usar o retângulo para desenhar uma imagem em outra posição da tela. A modificação do retângulo pode ser feita facilmente com o método move, como feito na linha 37 do exemplo da bola:

ballrect = ballrect.move(speed)

... sendo que o parâmetro passado para move é uma tupla contendo os incrementos a serem feitos em suas coordenadas.


Algumas das operações de superfície são usadas no exemplo. Nele se trabalha com duas superfícies: screen, que representa a tela da janela, e ball, que representa a imagem da bola. A cada ciclo de execução, a bola é desenhada numa posição da tela, como mostrado nas linhas 37 a 52 destacadas a seguir:

    #
    # move a bola de acordo com "speed". Isso muda as coordenadas do retângulo 
    # que a contém
    ballrect = ballrect.move(speed)

    # se limites horizontais de movimentação foram atingidos, então velocidade horizontal
    # é invertida
    if ballrect.left < 0 or ballrect.right > width:
        speed[0] = -speed[0]
    # se limites verticais de movimentação foram atingidos, então velocidade vertical
    # é invertida
    if ballrect.top < 0 or ballrect.bottom > height:
        speed[1] = -speed[1]

    # pinta toda a tela de preto
    screen.fill(black)

    # copia a imagem da bola para a região definida por "ballrect"
    screen.blit(ball, ballrect)


A cada vez que se move o retângulo da bola (e após refletir a bola nas paredes, caso necessário), pinta-se toda a tela de preto. Isso é fundamental para limpar a tela antes de redesenhar a bola:

screen.fill(black)

... e, em seguida, desenha-se a bola na nova posição ditada pelo retângulo ballrect:

screen.blit(ball, ballrect)

Assim, toda vez que se precisar desenhar uma superfície sobre outra, usa-se o método blit da superfície que será modificada.

Por fim, note que o display deve ser atualizado após redesenhar a bola, como se pode ver na linha 55:

pygame.display.flip()

Imagens e Sprites

Imagens são superfícies cujo conteúdo (seus pixels) são carregados de um arquivo. Diversos formatos de imagem são suportados. Uma imagem é criada usando a função load, tendo como resultado uma superfície que representa a imagem:

ball = pygame.image.load('ball.gif')

Após carregada, uma imagem pode ser usada como uma superfície qualquer, podendo inclusive ser modificada e salva novamente em arquivo (ver função save).

Sprites são elementos visíveis, que podem ser agrupados e desenhados ou removidos em conjuntos. Outras funcionalidades úteis são detecção de colisão ou sobreposição de elementos visuais, e controle da ordem em que são desenhados (para representar objetos que estão sobre outros). Apesar de, a rigor, ser um elemento importante para a criação de jogos, sprites fogem do escopo desta introdução. No entanto, a documentação fornece uma boa explicação.

Eventos

Jogos e aplicativos gráficos somente são funcionais se puderem interagir com o usuário de alguma forma. A interação pode ser por teclado, mouse, joystick ou outros dispositivos. Pygame trata as entradas desses dispositivos e as apresenta para aplicativos na forma de eventos.

Eventos representam cliques e movimentações de mouse, teclas pressionadas ou liberadas, ativação de temporizadores e acontecimentos de interesse (ex: uma requisição para terminação do programa). Eventos podem ser recebidos de forma bloqueante usando a função wait, ou verificados com a função get. No exemplo da bola, usa-se a função get para coletar todos os eventos pendentes antes de novamente movimentar e redesenhar a bola:

    for event in pygame.event.get():
        # apenas evento QUIT é considerado: ele faz com que a
        # aplicação termine. Demais eventos são ignorados
        if event.type == pygame.QUIT: sys.exit()

Como get retorna uma lista de eventos pendentes, pode-se iterá-la para tratar cada evento. No exemplo, trata-se apenas o evento pygame.QUIT, que representa uma requisição para terminação do programa, ignorando-se outros eventos que possam ter ocorrido. Assim, se o mouse foi movimentado ou clicado sobre a tela, nenhum efeito terá sobre o aplicativo, pois os eventos correspondentes n]ao são tratados. Os tipos de evento podem ser vistos na documentação, sendo alguns deles:

Tipo de evento Atributos Significado
QUIT none pedido de terminação do programa
ACTIVEEVENT gain, state Programa recebeu foco do mouse
KEYDOWN unicode, key, mod tecla pressionada
KEYUP key, mod tecla liberada
MOUSEMOTION pos, rel, buttons movimentação do mouse
MOUSEBUTTONUP pos, button botão do mouse pressionado
MOUSEBUTTONDOWN pos, button botão do mouse liberado
>= USEREVENT None evento gerado pelo próprio programa (ex: timer)

Cores

Cores são tuplas contendo os valores para vermelho, verde e azul (RGB). Por exemplo, as cores preta e branca podem ser definidas assim:

preta = (0,0,0)
branca = (255, 255, 255)

Os valores para vermelho, verde e azul podem ser obtidos com ferramentas específicas, como esta:


Pygame possui a classe Color, que auxilia algumas operações sobre cores.

Temporização

Jogos dependem de controle de tempo. O exemplo da bola mostra que é necessário temporizar sua movimentação, do contrário a bola se move tão rápido quanto o programa possa ser executado. Pygame possui no módulo time algumas facilidades para monitorar tempo e definir temporizadores. Temporizadores são úteis para dispararem eventos que sinalizem que um tempo de interesse acabou de transcorrer. Com isso, podem-se realizar coisas como apresentar uma imagem por 2 segundos, e então removê-la da tela.


A criação de um temporizador deve ser feita usando a função set_timer:

# programa um timer para daqui a 2 segundos. Ele vai gerar um evento
# do tipo USEREVENT+1
python.time.set_timer(USEREVENT+1, 2000)


Para tratar o evento desse timer, o programa deve ter algo assim em seu tratador de eventos:

for event in pygame.event.get():
  if event.type == USEREVENT+1:
    print('Passaram 2 segundos !')
    # desativa o timer
    pygame.time.set_timer(USEREVENT+1, 0)

Deve-se notar que o temporizador gera o evento periodicamente. Para desativá-lo deve-se chamar set_timer passando o tempo 0 (zero) como parâmetro.

Um exercício

  1. Experimente modificar o exemplo para que a bola seja paralisada na tela se um clique do mouse ocorrer sobre ela. Se um novo clique for feito, a bola volta a se movimentar.
  2. Modifique o exemplo para que se crie um jogo de arremesso:
    • A janela deve ser ampliada para ocupar toda a tela
    • A bola fica no canto inferior esquerdo
    • Um alvo é colocado em torno do canto superior direito
    • Com o mouse se clica na bola, faz-se um movimento de arremesso em direção ao alvo (mantendo o botão do mouse pressionado)
    • Ao se liberar o botão do mouse, ou quando a bola ultrapassar certo limiar, ela deve ser arremessada na direção do movimento
    • A velocidade do arremesso depende da velocidade com que se arrastou a bola
    • A direção do arremesso depende da direção com que se arrastou a bola
    • O movimento da bola sofre efeito da gravidade
    • Se a bola acertar o alvo, soma-se um ponto

Um jogo de memória

Jogo de memória é um jogo clássico. Uma variedade de tipos e quantidades de cartões podem ser usadas. Esse jogo é relativamente fácil de implementar com pygame. A figura a seguir mostra um jogo de memória rudimentar que demorou cerca de 4 horas para ser criado (começando-se do zero ... nem se conhecia pygame antes disso !).

Oficina-python-mem.png


Tente implementá-lo com o conteúdo apresentado nesta oficina. Tudo que é necessário para criá-lo foi informado. Para os cartões podem-se usar estes arquivos de imagem de cartas de baralho.