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".

quinta-feira, 28 de maio de 2009

Retomando os Trabalhos!

Olá pessoal! Faz algum tempo que não atualizo o blog, é que estive muiiittoooo ocupado com o trabalho...

Bom, hoje vou começar a entrar nos detalhes do nosso projeto, finalmente! Vou falar um pouco do pygame, a biblioteca Python utilizada no desenvolvimento de jogos. Esta biblioteca irá nos fornecer praticamente tudo o que precisamos para desenvolver um jogo em Python, no que se diz respeito a parte programática da coisa.

Como é sabido, na história do desenvolvimento de jogos quase sempre se utilizam linguagens de baixo nível, tais como C/C++. Isto ocorre devido ao fato da necessidade de desempenho que está implícita na programação de jogos.

No entanto, tal desempenho vem com um custo. O desenvolvedor passa a ter que tratar de detalhes que não têm relação com o desenvolvimento do jogo propriamente dito, tais como o gerenciamento de memória e o tratamento de erros. Outro detalhe é que no desenvolvimento de jogos sempre temos algumas características que estão sempre presentes, tais como os sprites - figuras animadas ou não, e as rotinas que manipulam estes sprites, utilização de sons, recursos de rede, etc. Tendo isto em mente, surgiram várias bibliotecas dedicadas a facilitar a vida dos desenvolvedores de jogos, oferecendo o suporte básico a tais funcionalidades. Este é o caso da biblioteca SDL, a qual é a base da biblioteca pygame.

Ou seja, temos a biblioteca SDL, totalmente otimizada para linguagens de baixo nível, e temos o pygame, que possibilita o acesso a SDL através da linguagem Python. Isto torna o desenvolvimento de jogos utilizando Python uma realidade, já que temos apenas que nos preocupar com a criação do jogo em si, e, uma vez que com Python temos uma forma simples de expressar nossas ideias, deixando os detalhes de baixo nível para o pygame, podemos focar apenas na parte criativa.

Conceitos Básicos

O loop principal de um jogo é bastante simples. Basicamente temos que tratar os eventos, tais como o movimento do mouse ou uma tecla ser pressionada ou a recepção de dados via rede, executar a inteligência artificial e então o desenho do plano de fundo e de personagens visíveis. Vejamos abaixo como isso é feito de um modo geral:

01. Inicializar estruturas internas
02. Carregar dados do jogo
03. Repetir:
04.    Capturar e tratar eventos
05.    Executar a inteligência artificial
06.    Desenhar plano de fundo
07.    Desenhar personagems
08.    Emitir sons se necessário
09. Terminar o loop se o usuário deseja terminar o jogo.

Este pseudo código resume o laço principal de qualquer jogo, por mais avançado que seja, sempre terá um loop parecido com este.

No próximo post pretendo mostrar os passo para a criação de um jogo bem simples, onde o jogador controla uma nave espacial e deve derrotar os inimigos, no estilo "vertical scroller", ou seja, os inimigos irão aparecer no topo da tela e seguirão para baixo tentando acertar a nave do jogador. Espero postar uma primeira parte ainda hoje, ou no máximo amanhã. Então fiquem ligado!