sexta-feira, 29 de maio de 2009

Loop Básico com PyGame

Bom Dia! Vou postar hoje o código de um loop básico para o jogo com naves usando PyGame. Para instalar a biblioteca PyGame vá até o site www.pygame.org e siga as instruções.

Vejamos abaixo o código:

# coding: utf8
# game.py

import pygame
import random

from pygame.locals import *

class Game(object):
    
    def __init__(self, size):
        pygame.init()
        flags = DOUBLEBUF
        self.screen = pygame.display.set_mode(size, flags)
        self.screen_size = self.screen.get_size()
        
        pygame.mouse.set_visible(False)
        pygame.display.set_caption('Meu Primeiro Jogo em Python')
        
        self.run = True
        self.list = {
            'player': pygame.sprite.RenderPlain(),
            'enemy': pygame.sprite.RenderPlain(),
            'fire': pygame.sprite.RenderPlain(),
            'enemy_fire': pygame.sprite.RenderPlain(),
        }
        self.player = None
        self.background = None
        self.actors = None
    
    def update_actors(self):
        if self.background is not None:
            self.background.update()
        
        for actor in self.list.values():
            actor.update()
    
    def draw_actors(self):
        if self.background is not None:
            self.background.draw(self.screen)
        else:
            self.screen.fill(0)
        
        for actor in self.list.values():
            actor.draw(self.screen)
    
    def act_actors(self):
        pass
    
    def manage(self):
        pass
    
    def handle_events(self):
        player = self.player
        for event in pygame.event.get():
            t = event.type
            
            if t in (KEYDOWN, KEYUP):
                k = event.key
            
            if t == QUIT:
                self.run = False
            elif t == KEYDOWN and k == K_ESCAPE:
                self.run = False
            elif t == KEYUP:
                pass
    
    def loop(self):
        clock = pygame.time.Clock()
        
        while self.run:
            clock.tick(1000)
            
            self.handle_events()
            self.manage()
            
            self.act_actors()
            self.update_actors()
            self.draw_actors()
            
            pygame.display.flip()

if __name__ == '__main__':
    game = Game( (640, 480) )
    game.loop()

Este será o corpo de nossa classe principal do jogo. Salve este arquivo e o execute. Você verá que o PyGame abrirá uma janela com tamanho 640x480 preenchida com a cor preta. Ao pressionar a tecla ESC a aplicação é terminada.

Pode não parecer, mas com estas poucas linhas de código já temos a estrutura básica para o nosso jogo. No construtor da class "Game" nós inicializamos a tela do jogo, utilizando o parâmetro "DOUBLEBUF", mais a diante explico o motivo de se utilizar este parâmetro. Então informamos um título para a janela e que desejamos que o mouse não seja visível dentro da janela do jogo.

As variáveis "run" e "list" são então inicializadas. A primeira indica se o jogo deve continuar ou não. A segunda guarda lista de sprites em quatro categorias. A classe "pygame.sprite.RenderPlain" é responsável por guardar as lista com os sprites e ela também será responsável por desenhar esta lista na tela. Na categoria "player" teremos apenas o sprite da nossa nave. Em "enemy" teremos os sprites das naves inimigas. Em "fire" adicionaremos os sprites dos nossos tiros, e, finalmente, em "enemy_fire" adicionaremos os sprites dos tiros das naves inimigas.

Temos então as variáveis "player" e "background", que serão exatamente objetos que representam o jogador e o plano de fundo de nosso jogo.

O método "update_actors" é o responsável por atualizar o estado de nossos objetos do jogo. Primeiro nós atualizamos o estado do plano de fundo, se houver um, e então atualizamos os atores, que são os objetos na nossa lista de sprites.

No método "draw_actors" temos um comportamento semelhante ao método "update_actors", mas nesse caso iremos escrever os pixels dos nossos plano de fundo e sprites na tela.

Em "act_actors" por enquanto não iremos ter nenhum código, mas é aqui que nós iremos tratar a ação de nosso jogo. Esta parte será explicada em um próximo post. O método "manage" será responsável por controlar se adicionamos mais inimigos ou não dependendo do estado do jogo. Por enquanto não irei mostrar o código desta parte também.

Em "handle_events" nós perguntamos ao pygame quais eventos ocorreram e então os tratamos. Por enquanto só temos interesse nos eventos do tipo "QUIT" e "KEYDOWN". Caso o evento "QUIT" aconteça ou no caso de "KEYDOWN" com a tecla "ESC" sendo pressionada, nós ajustamos a variável "run" para "False", o que fará que o loop principal de nosso jogo termine.

Chegamos, então, ao loop principal do jogo. Aqui criamos a variável "clock". Esta variável irá controlar a velocidade do jogo, de forma que o jogo não fique demasiado lento em máquinas menos potentes nem demasiado rápido em máquinas mais potentes.

Então temos nosso loop. Nós primeiros tratamos os possíveis eventos, depois atualizamos o estado do jogo para só então desenharmos os objetos na tela.

A última linha deste loop: "pygame.display.flip()" é bem interessante. Lembram do parâmetro "DOUBLEBUF" que utilizamos anteriormente na inicialização da tela? Pois aquele parâmetro informa ao pygame que, quando escrevermos os pixels na tela, na verdade estaremos escrevendo pixels numa espécie de buffer, que não é mostrado imediatamente na tela. Sendo assim, quando chamamos a função "pygame.display.flip()" este buffer é inteiramente copiado para a tela e a superfícies que estava sendo mostrada se tornará o novo buffer. Esse é o motivo da função se chamar "flip", pois ela "troca" as superfície atual com a temporária.

O "double buffering" como chamamos esta técnica é utilizado para evitar que durante o processo de desenho dos pixels o usuário veja imagens aparecendo uma após a outra na tela, causando um efeito indeseável e até mesmo problemas como o de sprites que são desenhados sobre outros sprites.

Bom, é isso! Em breve trarei mais um post com a classe "Background" que irá mostrar estrelas no plano de fundo, movimentandos no estilo dos famosos "vertical scrollers".

4 comentários:

jorgerosa disse...

Belo trabalho. Ajudou-me muito. Obrigado!

Unknown disse...

não tem nada a ver, mas como vc faz o destacador de sintaxe do teu blog???

João Paulo Farias disse...

Obrigado pelos agradecimentos.

Denis, para fazer o syntax highlight, usei o google prettify, que você pode encontrar em: http://code.google.com/p/google-code-prettify/

Até mais!

--
João Paulo Farias.

Anônimo disse...

Eu não estou conseguindo fechar a janela, nem clicando no 'X', nem teclado 'ESC'.

python 2.7
pygame 1.9.1