Desenvolvimento - Python
Introdução ao PyGame
Nesse artigo vamos criar um jogo bem simples com PyGame, levemente baseado no clássico "Arkanoid".
por Rodrigo VieiraNesse artigo vamos criar um jogo bem simples com PyGame, levemente baseado no clássico "Arkanoid", ou seja, um objeto que quica pela tela e que devemos rebater com uma plataforma. Quando pronto, o jogo deverá parecer assim:
1) Elementos básicos
Como você pode ver acima, o nosso jogo terá 3 elementos principais: o "bouncer", que é a plataforma vermelha no fundo da tela, que rebate a "bola" (bem, o hambúrguer, por falta de imagem melhor!). O terceiro elemento é a tela do jogo em si, onde a ação ocorre.
2) Classe "Bouncer"
Nossa primeira classe é muito simples, herdada da classe Sprite nativa do PyGame. Um sprite é um elemento gráfico do jogo, e possui 2 propriedades importantes: image e rect. Image é a imagem em si, carregada do disco (os principais formatos são suportados, tais como JPG, GIF, PNG e BMP) e rect representa o retângulo virtual que contém a imagem (imagine um retângulo circunscrito à imagem). Alterando as propriedades centerx, centery, top, bottom, left e right de rect podemos posicionar o sprite onde quisermos na tela.
O método update é executado a cada loop do nosso jogo para mover o sprite para esquerda ou para direita, através da função move_ip, que aceita 2 parâmetros, x e y, indicando quantos pixels queremos mover o objeto no eixo x (horizontal) e y (vertical). Vale lembrar que no nosso caso a origem do eixo está no canto superior esquerdo da tela, logo, ao executarmos move_ip(1,2) estaremos movendo o sprite 1 pixel para a direita e 2 pixes para baixo.
class Bouncer(pygame.sprite.Sprite): def __init__(self, startpos): pygame.sprite.Sprite.__init__(self) #direcao: 1=direita, -1=esquerda self.direction = 1 #carrega a imagem e a posiciona na tela self.image, self.rect = load_image("bouncer.gif") self.rect.centerx = startpos[0] self.rect.centery = startpos[1] def update(self): #multiplicamos x por 3 pro bouncer mover-se #um pouco mais rápido! self.rect.move_ip((self.direction*3,0)) #se o bouncer atingir os limites da tela, #invertemos a sua direcao if self.rect.left < 0: self.direction = 1 elif self.rect.right > width: self.direction = -1
3) Classe "Ball"
A nossa segunda classe será bem similar. A única diferença é no método update, que faz com que a bola "quique" pela tela, quando atingir o topo ou os lados. Caso a bola atinja a parte inferior da tela, ela "morre" e é reinicializada na posição inicial.
class Ball(pygame.sprite.Sprite): """classe para a bola""" def __init__(self, startpos): pygame.sprite.Sprite.__init__(self) self.speed = [2,2] #carrega a imagem e a posiciona na tela self.image, self.rect = load_image("ball.gif") self.rect.centerx = startpos[0] self.rect.centery = startpos[1] #salva a posicao inicial para ser reutilizada #quando a bola sair da tela pelo fundo self.init_pos = startpos def update(self): self.rect.move_ip(self.speed) #se a bola atingir os lados da tela, inverte a #direcao horizontal (x) if self.rect.left < 0 or self.rect.right > width: self.speed[0] = -self.speed[0] #se a bola atingir o topo da tela, inverte a #posicao vertical (y) if self.rect.top < 0: self.speed[1] = -self.speed[1] #se a bola atingir o fundo da tela, reseta #a sua posicao if self.rect.bottom > height: self.rect.centerx = self.init_pos[0] self.rect.centery = self.init_pos[1]
4) Detectando e reagindo a eventos
O nosso jogo também precisará detectar quando o jogador apertar as setas para direita e esquerda do teclado, para alterar a direção do bouncer, bem como detectar se o jogador fechou a janela, para terminar o jogo. Isso é feito através do seguinte trecho de código:
#checa eventos de teclado for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: bouncer.direction = -1 if event.key == pygame.K_RIGHT: bouncer.direction = 15) Detectando colisões
Finalmente, precisamos detectar se a bola atingiu o nosso bouncer, e nesse caso inverter a direção vertical da bola. Existem várias formas de detectarmos colisões de sprites, nesse caso usaremos a mais simples de todas, sprite.rect.colliderect(sprite.rect), que retorna "1" caso os retângulos dos 2 sprites estejam se sobrepondo (ou seja, colidindo).
#checa se a bola colidiu no bouncer, e caso sim inverte a #direcao vertical da bola
if bouncer.rect.colliderect(ball.rect): if ball.speed[1]> 0: ball.speed[1] = -ball.speed[1]
6) Desenhando tudo: "blitting"
A última parte do nosso programa desenhará todos os objetos na tela a cada loop do jogo, de acordo com suas posições atuais (atualizadas através do método update() de cada um).
Após calculadas as novas posições, a forma mais eficiente de atualizarmos a tela é preenchê-la com o fundo preto, para que os objetos antigos (da iteração anterior) sejam apagados: isso é muito mais simples do que ter que apagá-los um a um. O método blit renderiza cada objeto na tela, e quando todos estiverem prontos o método flip irá efetivamente atualizar a tela para o jogador.
#atualiza os objetos ball.update() bouncer.update() #redesenha a tela screen.fill(black) screen.blit(ball.image, ball.rect) screen.blit(bouncer.image, bouncer.rect) pygame.display.flip()
7) Finalizando
Pronto! Agora já temos todos componentes do nosso jogo. Abaixo temos o código completo, que também pode ser baixado aqui. Sinta-se livre para terminar o jogo! Se você quiser checar um jogo um pouco mais complexo (mas não muito), dê uma olhada aqui.
import sys, pygame, os pygame.init() size = width, height = 600, 400 screen = pygame.display.set_mode(size) black = 0, 0, 0 class Bouncer(pygame.sprite.Sprite): def __init__(self, startpos): pygame.sprite.Sprite.__init__(self) #direcao: 1=direita, -1=esquerda self.direction = 1 #carrega a imagem e a posiciona na tela self.image, self.rect = load_image("bouncer.gif") self.rect.centerx = startpos[0] self.rect.centery = startpos[1] def update(self): #multiplicamos x por 3 pro bouncer mover-se #um pouco mais rápido! self.rect.move_ip((self.direction*3,0)) #se o bouncer atingir os limites da tela, #invertemos a sua direcao if self.rect.left < 0: self.direction = 1 elif self.rect.right > width: class Ball(pygame.sprite.Sprite): """classe para a bola""" def __init__(self, startpos): pygame.sprite.Sprite.__init__(self) self.speed = [2,2] #carrega a imagem e a posiciona na tela self.image, self.rect = load_image("ball.gif") self.rect.centerx = startpos[0] self.rect.centery = startpos[1] #salva a posicao inicial para ser reutilizada #quando a bola sair da tela pelo fundo self.init_pos = startpos def update(self): self.rect.move_ip(self.speed) #se a bola atingir os lados da tela, inverte a #direcao horizontal (x) if self.rect.left < 0 or self.rect.right > width: self.speed[0] = -self.speed[0] #se a bola atingir o topo da tela, inverte a #posicao vertical (y) if self.rect.top < 0: self.speed[1] = -self.speed[1] #se a bola atingir o fundo da tela, reseta #a sua posicao if self.rect.bottom > height: self.rect.centerx = self.init_pos[0] self.rect.centery = self.init_pos[1] def load_image(name): """carrega uma imagem na memoria""" fullname = os.path.join("images", name) try: image = pygame.image.load(fullname) except pygame.error, message: print "Cannot load image:", fullname raise SystemExit, message return image, image.get_rect() def main(): #cria os nossos objetos (bola e bouncer) ball = Ball([100,100]) bouncer = Bouncer([20,395]) pygame.display.set_caption("Bouncer!") clock = pygame.time.Clock() while 1: #garante que o programa nao vai rodar a mais que 120fps clock.tick(120) #checa eventos de teclado for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: bouncer.direction = -1 if event.key == pygame.K_RIGHT: bouncer.direction = 1 #checa se a bola colidiu no bouncer, e caso #sim inverte a direcao vertical da bola if bouncer.rect.colliderect(ball.rect): if ball.speed[1] > 0: ball.speed[1] = -ball.speed[1] #atualiza os objetos ball.update() bouncer.update() #redesenha a tela screen.fill(black) screen.blit(ball.image, ball.rect) screen.blit(bouncer.image, bouncer.rect) pygame.display.flip() if __name__ == "__main__": main()