Capítulo 17 - Pong remix

Este capítulo não pretende introduzir muitos elementos novos mas, em vez disso, propor o desenvolvimento, passo-a-passo, de uma versão do velho jogo Pong, desenvolvido originalmente em 1972 por Allan Alcorn para a Atari e até hoje um ícone dos videogames.

Para começar, visite a página http://www.ponggame.org/ e comece a planejar como será a sua própria recriação do jogo. Tenha o Zen do Python em sua mente ao escrever o seu código e lembre-se que na web você encontra informações que vão muito além do que cabe nesse livro. Revise o capítulo 14 para lembrar como qualificar suas buscas no Google, ou veja um resumo sobre como fazer isso no endereço http://brodtec.com/google.

Abaixo, o remix do jogo Pong desenvolvido nesse capítulo. Copie-o para dentro de seu editor preferido, leia os comentários dentro do programa, salve-o com o nome pong.py e execute-o com o comando python pong.py (você vai precisar da fonte 8-Bit-Wonder).

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# pong.py - versão 0.09 - Cesar Brod - 23/09/2013
#
# Para executar esse programa, você deve ter a linguagem Python
# instalada em seu sistema, assim como a biblioteca Pygame.
#
# Usuários de sistemas Debian e seus derivados podem usar seu
# gerenciador de pacotes e instalar python e python-pygame ou, na linha
# de comandos digitar:
#
# sudo apt-get install python python-pygame
#
# Este programa foi desenvolvido para o livro "Aprenda a programar - 
# a arte de ensinar o computador", de Cesar Brod, Editora Novatec, São Paulo, 2013.
#
import sys  # reconhece eventos do sistema
import pygame 
from pygame.locals import *  # constantes para a interação com o sistema
pygame.init()  # inicializa as funções da biblioteca Pygame
# Define o tamanho da tela a partir da opção do usuário
print 'Deseja jogar pong em tela cheia?'
print 'Pressione a tecla S, caso positivo ou qualquer,'
print 'outra tecla para jogar em uma janela.'
# Código reaproveitado de 
# http://docs.python.org/2/faq/library#how-do-i-get-a-single-keypress-at-a...
# para ler apenas uma tecla pressionada
import termios, fcntl, os
fd = sys.stdin.fileno()
oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
escolha = False
try:
    while not escolha:
        try:
            tela_cheia = sys.stdin.read(1)  # lê o caractere
            tela_cheia = (tela_cheia).lower()  # converte a resposta para letras minúsculas
            escolha = True
        except IOError: pass
finally:
    termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
# Final do código reaproveitado
if tela_cheia == 's':
    tamanho = largura, altura = 0,0  # tela cheia
else:
    tamanho = largura, altura = 500, 420  # janela
preto = 0, 0, 0  # RGB para a cor preta
branco = 255, 255, 255  # RGB para a cor preta
cenario = pygame.display.set_mode(tamanho)  # cria o cenário do jogo
clock = pygame.time.Clock()  # permite o controle de velocidade da animação
tamanho = largura, altura = cenario.get_size()  # obtém a dimensão real da tela
placar_e = placar_d = 0  # o placar inicia zerado
# Fonte criada por  Joiro Hatagaya (leia o arquivo 8bit_font/README.TXT)
fonte = pygame.font.Font("8bit_font/8-BIT WONDER.TTF", altura/7)
fonte_msg = pygame.font.Font(None, altura/10)
fonte_menor = pygame.font.Font(None, altura/20)
vitoria_texto = fonte_msg.render('VENCEDOR', 1, branco)
fim_texto = fonte_menor.render('Tecle F para sair do jogo', 1, branco)
continua_texto1 = fonte_menor.render('ou qualquer outra', 1, branco)
continua_texto2 = fonte_menor.render('tecla para continuar', 1, branco)
# Classe básica para todos os personagens do Pong
class personagem(pygame.sprite.Sprite):
    def __init__(self, l_personagem, a_personagem):
        pygame.sprite.Sprite.__init__(self)
        self.l_personagem = l_personagem
        self.a_personagem = a_personagem
        self.image = pygame.Surface([self.l_personagem, self.a_personagem])
        self.image.fill(branco)
        self.rect = self.image.get_rect()  
# Instancia a bola a partir de personagem e inicializa suas variáveis
bola = personagem(largura/40, largura/40)
coordenadas_bola = [largura/2, altura/2]
velocidade_bola = [2*largura/500, 2*largura/500]
# Instancia a raquete da esquerda a partir de personagem e inicializa suas variáveis
raquete_e = personagem(largura/40, altura/10)
coordenadas_raquete_e = [largura/25, altura/2]
# Instancia a raquete da direita a partir de personagem e inicializa suas variáveis
raquete_d = personagem(largura/40, altura/10)
coordenadas_raquete_d = [largura - largura/25, altura/2]
# A velocidade será a mesma para ambas as raquetes
velocidade_raquete = [altura/420, 2*altura/420]
# Inicializa um grupo de sprites para a animação
personagens = pygame.sprite.Group()
# Adiciona a bola ao grupo de sprites personagens
personagens.add(bola)
# Adiciona as raquetes ao grupo de sprites personagens
personagens.add(raquete_e)
personagens.add(raquete_d)
while 1:  # Repetição infinita, qualquer condição que seja sempre verdadeira
# As linhas abaixo verificam se a ação de fechamento da janela (QUIT) foi
# chamada e, caso positivo, fecha o programa
    print
    print 'Estou na repetição infinita'
    print
    print 'Coordenadas da bola:    x = ' + str(coordenadas_bola[0]) + ', y = ' + str(coordenadas_bola[1])
#    print 'Coordenadas da raquete: x = ' + str(coordenadas_raquete[0]) + ', y = ' + str(coordenadas_raquete[1])
    print 'Velocidade da bola:     x = ' + str(velocidade_bola[0]) + ', y = ' + str(velocidade_bola[1])
    print 'Largura = ' + str(largura) + ', Altura = ' + str(altura)
    for event in pygame.event.get():
        if event.type == pygame.QUIT: 
            sys.exit()
# Movimento da bola
    bola.rect.center = (coordenadas_bola[0], coordenadas_bola[1])
    coordenadas_bola[0] = coordenadas_bola[0] + velocidade_bola[0]
    coordenadas_bola[1] = coordenadas_bola[1] + velocidade_bola[1]
    if coordenadas_bola[0] > largura or coordenadas_bola[0] < 0:
        print 'A bola mudou de direção no eixo x'
        pygame.mixer.Sound('/usr/share/pyshared/pygame/examples/data/boom.wav').play()
        if velocidade_bola[0] > 0:  # Ponto para o jogador da esquerda
            placar_e += 1
        else:  # Ponto para o jogador da direita
            placar_d += 1 
        velocidade_bola[0] = -velocidade_bola[0]
        coordenadas_bola[0] = largura/2
    if coordenadas_bola[1] > altura or coordenadas_bola[1] < 0:
        print 'A bola mudou de direção no eixo y'        
        velocidade_bola[1] = -velocidade_bola[1]
        pygame.mixer.Sound('/usr/share/scratch/Media/Sounds/Effects/Pop.wav').play()
# Movimento das raquetes
# Habilita a verificação de teclas pressionadas
    tecla = pygame.key.get_pressed()
# Usa as setas para mover a raquete direita
    raquete_d.rect.center = (coordenadas_raquete_d[0], coordenadas_raquete_d[1])
    if (tecla[K_UP]):
        print 'A seta para cima pressionada'
        coordenadas_raquete_d[1] = coordenadas_raquete_d[1] - velocidade_raquete[1]
    elif (tecla[K_DOWN]):
        print 'A seta para a baixo foi pressionada'
        coordenadas_raquete_d[1] = coordenadas_raquete_d[1] + velocidade_raquete[1]
# Usa as teclas A e Z para mover a raquete direita
    raquete_e.rect.center = (coordenadas_raquete_e[0], coordenadas_raquete_e[1])
    if (tecla[K_a]):
        print 'A tecla A foi pressionada'
        coordenadas_raquete_e[1] = coordenadas_raquete_e[1] - velocidade_raquete[1]
    elif (tecla[K_z]):
        print 'A tecla Z foi pressionada'
        coordenadas_raquete_e[1] = coordenadas_raquete_e[1] + velocidade_raquete[1]
# Teste de colisão
    if pygame.sprite.collide_rect(bola, raquete_e) or  pygame.sprite.collide_rect(bola, raquete_d) == True:
        print 'Detectei uma colisão entre bola e raquete! '
        pygame.mixer.Sound('/usr/lib/libreoffice/share/gallery/sounds/laser.wav').play()
        velocidade_bola[0] = -velocidade_bola[0]
        if velocidade_bola[0] > 0:
            coordenadas_bola[0] = coordenadas_bola[0] + largura/80
        if velocidade_bola[0] < 0:
            coordenadas_bola[0] = coordenadas_bola[0] - largura/80
# *** PAUSA ***
# A linha abaixo introduz uma pausa que pode ser interessante em casos de
# teste. Para que a pausa aconteça, remova o # do começo da linha a seguir.
#
#    raw_input("Presione Enter para continuar...")
#
# Durante uma pausa, ou a qualquer momento da execução do programa, pode 
# ser importante exibir, na tela, suas variáveis, a fim de testar a lógica
# do programa e os resultados esperados
# 
#    print 'variavel_1 = ' + str(variavel_1) + ' | variavel_2 = ' + str(variavel_2) 
    cenario.fill(preto)  # pinta o cenário de preto  
# Linha divisória
    y = altura / 200
    traco = altura / 50
    incremento = altura / 30
    while y <= altura:
        pygame.draw.line(cenario, branco, (largura/2, y), (largura/2, y+traco), largura/200)
        y += incremento
# Placar
    placar_e_texto = fonte.render(str(placar_e), 1, branco)
    placar_d_texto = fonte.render(str(placar_d), 1, branco)
    cenario.blit(placar_e_texto, (largura/2 - largura/5, altura/20))
    cenario.blit(placar_d_texto, (largura/2 + largura/10, altura/20))
    if placar_e >= 10 or placar_d >= 10:  # Fim de jogo
        pygame.mixer.Sound('/usr/lib/libreoffice/share/gallery/sounds/romans.wav').play()
        if placar_e > placar_d:
           cenario.blit(vitoria_texto, (largura/10, altura/4)) 
        else: 
           cenario.blit(vitoria_texto, (largura/2 + largura/10, altura/4)) 
        cenario.blit(fim_texto, (largura/2 + largura/30, altura/2 + altura/10)) 
        cenario.blit(continua_texto1, (largura/2 + largura/30, altura/2 + altura/6)) 
        cenario.blit(continua_texto2, (largura/2 + largura/30, altura/2 + altura/5)) 
        pygame.display.flip()
        placar_e = placar_d = 0  # reinicia o placar
        resposta = False
        while not resposta:
            for event in pygame.event.get():
                tecla = pygame.key.get_pressed()
                if (tecla[K_f]):
                    print 'A tecla F foi pressionada'
                    sys.exit()
                elif event.type == KEYDOWN:
                    print 'Outra tecla foi pressionada'
                    resposta = True
    personagens.draw(cenario)  # imprime os sprites no cenário
    pygame.display.flip()  # imprime o cenário atualizado
    clock.tick(60)  # animações rodam a 60 quadros por segundo

Atenção: O serviço de compartilhamento utilizado para armazenar os arquivos desse livro, o UbuntuOne, foi descontinuado. O novo link para os arquivos referenciados no livro são os seguintes:

raquete.png
bola.png
imagem da máquina virtual
explode.tar.bz2
bolas.tar.bz2



Design: Dobro Comunicação. Desenvolvimento: Brod Tecnologia. Powered by Drupal