sábado, julho 14, 2007

Mini-Aulas PyGame (parte 1)


Tutorial enviado por Flavio Ribeiro.
Para enviar o seu, mande para o email no perfil ao lado


Olá, hoje vou introduzir poucos conceitos que aprendi pra desenvolver games usando pygame. Vou fazer um pequeno joguinho onde uma nave se move na tela e dá tiros. No fim falo mais sobre esse 'jogo'.

1. Antes de mais nada, instale o pygame. Observe que se você usar alguma distro debian-based, basta um apt-get install python-pygame que ele dá conta do resto. Não sei em outros gerenciadores de pacotes (yum, emerge, etc) mas se não conseguirem usa-los, basta fazer download do tarball.

2. Teste se já tá funcionando importando no interpretador python:

root@nerdy:/# python
Python 2.5.1 (r251:54863, May 2 2007, 16:56:35)
[GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pygame
>>>
>
Se o interpretador não chiar, estamos prontos.

3. Vamos agora fazer nosso 'joguinho'. Comece importando os módulos necessários:
import pygame, sys,os
from pygame.locals import *

__obsnãomuitoimportante__: O sys e os vou mostrar porque importei mais na frente, já o pygame.locals eu importei trazendo pro namespace pra economizar digitação.

4. Inicialize o pygame usando o metodo init():

pygame.init()

5. Agora sim, vamos por a mão na massa. Sete o tamanho da telinha do seu jogo e o título (que vai ficar na barrinha):

window = pygame.display.set_mode((200, 200))
pygame.display.set_caption('Meu primeiro jogo!')

6. Sete as váriaveis que vamos usar. Duas que indicarão a posição da nave (x_position e y_position), uma (step) que indica quantos pixels nossa nave vai se mexer quando o usuário apertar pros lados, e outra (shotspeed) que indica a velocidade do tiro.

global x_position, y_position, step
x_position, y_position = 50,100
step = 5
shotspeed = 1

obs_importante: O global indica que modificaremos os valores dessas variáveis dentro de funções. Para saber mais clique aqui.

7. Hora de 'carregar' nossas imagens. Aqui vou carregar o background, a nave e a imagem do tiro ok? Primeiro passo o endereço completo de onde a imagem se encontra (tá aí o os sendo usado!), depois dou um pygame.image.load pra carregar a imagem em uma instância.

background_draw = os.path.join("/home/bioinfo/pygame/mygames","tile3.png")
background = pygame.image.load(background_draw)

ship_draw = os.path.join("/home/bioinfo/pygame/mygames","nave.png")
ship = pygame.image.load(ship_draw)

shot_draw = os.path.join("/home/bioinfo/pygame/mygames","tiro.png")
shot = pygame.image.load(shot_draw)

8. Pra que o background e a nave apareça na tela é necessário que você desenhe (blit) ele. Mas não é só isso, tudo que você for desenhar ou atualizar no pygame você precisa atualizar (flip) a tela. No blit() você passa como argumentos a instância do pygame.image.load e a posição em coordenadas de pixels.

window.blit(background, (0,0)) # Desenha o background na tela. O background tem o mesmo tamanho setado por nós no passo 5)
window.blit(ship, (x_position,y_position)) # Desenha a nave
pygame.display.flip() # Recarrega a janela

9. Pronto, agora o que precisamos é de um laço infinito recebendo todos os eventos do gamer (aperta botão K, aperta botão SPACE, clica com o botão direito do mouse, etc...), e o pygame é bem eficiente nisso. Tudo que você precisa fazer é:

while True:
events = pygame.event.get()

O pygame.event.get() está atribuinto a variavel events uma lista com os eventos que estão acontecendo. Então, quando essa lista não for vazia, alguem apertou ou moveu algo. Vamos tratar esse evento!

for event in events:
if event.type == KEYDOWN and event.key == K_LEFT:
x_position -= step
window.blit(background, (0,0))
window.blit(ship, (x_position,y_position))
pygame.display.flip()

if event.type == KEYDOWN and event.key == K_RIGHT:
x_position += step
window.blit(background, (0,0))
window.blit(ship, (x_position,y_position))
pygame.display.flip()

if event.type == KEYDOWN and event.key == K_SPACE:
shot_pos_x, shot_pos_y = x_position, y_position
while shot_pos_y > 0:
window.blit(background, (0,0))
window.blit(ship, (x_position,y_position))
window.blit(shot, (shot_pos_x,shot_pos_y))
shot_pos_y -= shotspeed
pygame.display.flip()
window.blit(background, (0,0))
window.blit(ship, (x_position,y_position))
pygame.display.flip()

if event.type == QUIT:
sys.exit(0)

Calma, calma. Vou explicar tudinho. Esse for trata os eventos como falei acima, e se o tipo do evento for KEYDOWN (alguem pressionou uma tecla) e essa tecla for setinha-pra-esquerda (K_LEFT) nós alteramos a posição da nave em cinco pixels pra esquerda (diminuimos o x_position com o step), apagamos tudo redesenhando o background por cima de tudo, desenhamos a nave novamente só que no novo lugar e damos um 'refresh' na tela, sacou? O mesmo com o K_RIGHT.

Já com o K_SPACE, que é quando o usuário apertar espaço, nós setamos a posição do tiro que vai partir da nave (ou seja, tem que estar na mesma posição da nave que é x_position e y_position) e fazemos um while dizendo que enquanto a posição do tiro no eixo Y for maior que 0 (ou seja, enquanto o tiro não encostar na aresta superior da tela) o jogo precisa desenhar o background, desenhar a nave, desenhar o tiro e diminuir 1 pixel da posição atual do tiro, que é pra fazer ele subir. Dessa forma, uma hora o tiro vai chegar no topo da tela e sair fora do while. Aí o joguinho redesenha o background e a nave só e dá uma atualizada na tela pra que o tiro suma.

O ultimo evento é só pra o X do canto superior direito da tela funcionar.

Algumas Observações: Existem várias coisas nesse código que precisam ser melhoradas. Esse while do tiro precisa ser refeito porque enquanto o tiro viaja pela telinha nossa nave fica presa, esperando o tiro sumir da tela para que retomemos o controle dela. Isso é um erro.

Outra coisa que preciso aprender é a atualização (flip) da tela. Existe um outro método mais eficiente que é o update(), e usamos ele quando queremos atualizar apenas partes da tela. Essa atualização por partes é chamada de dirty-rect, e ela desenha apenas retângulos de background onde aconteceram movimentos.

No geral é mais ou menos isso. O código completo está aqui e se você quiser ver esse código funcionando pegue as imagens aqui. Não esqueca de modificar o caminho das imagens no comecinho do código. Quando eu for fazendo as otimizações e adicionando funcionalidades ao código vou criando mais tutoriais como esse.

sexta-feira, julho 06, 2007

Python e MySQL

Depois de algum tempo sem postar aqui no blog, vou mostrar um pouco sobre o modulo de Python para acessar um banco de dados MySQL, é o MySQLdb.

No Debian, Ubuntu, etc.... aptitude install python-mysqldb.

Eu presumo que você já saiba algo sobre MySQL, pois não vou falar sobre ele. Bem, depois depois do módulo baixado e instalado, vamos ver se ta funcionando:

>>> import MySQLdb
>>> print MySQLdb.__doc__
MySQLdb - A DB API v2.0 compatible interface to MySQL.

This package is a wrapper around _mysql, which mostly implements the
MySQL C API.

connect() -- connects to server

See the C API specification and the MySQL documentation for more info
on other items.

For information on how MySQLdb handles type conversion, see the
MySQLdb.converters module.

Tudo bem. Para se conectar ao banco é bem simples:

conexao = MySQLdb.connect('host', 'usuario', 'senha', 'banco')

Depois de estabelecida a conexão, é preciso criar um objeto "cursor", é ele quem vai executar os comandos SQL, assim como rebecer os dados que um comando SELECT por exemplo, vou mostrar adiante.
Primeiro vamos criar uma tabela que guarda um nome e um email, será a tabela Dados (o banco deve ser previamente criado):

import MySQLDB

conexao = MySQLdb.connect('host', 'usuario', 'senha', 'banco')
cursor = conexao.cursor()

cursor.execute('''create table Dados (
id int not null auto_increment primary key,
nome varchar(200) not null,
email varchar(100) not null );
''')

cursor.execute('''insert into Dados(nome, email)
values("igor", "igor@v2windcenter.com");
''')
cursor.close()
conexao.close()

O código acima vai criar uma tabela, e adicionar dados nessa tabela. Agora vamos retirar dados do banco, usando o interpretador mesmo:

>>> cursor.execute("SELECT * FROM Dados WHERE nome='igor'")
1L
>>> dados = cursor.fetchall()
>>> print dados
((1L, 'igor', 'igor@v2windcenter.com'),)

o método fetchall() do cursor, retorna todas as linhas que combinam com o SELECT que foi dado acima, observe que é uma tupla de tuplas...

>>> print dados[0][1]
igor
>>> print dados[0][2]
igor@v2windcenter.com

Como no SELECT foi usado *, para selecionar toda a linha, o retorno vai ser uma tupla de linhas que batem com a condição, mas vamos ver como seria para pegar apenas o nome (lembrando que nós temos a tabela dados, quem tem as colunas id, nome e email):

>>> cursor.execute("SELECT email FROM Dados WHERE nome='igor'")
1L
>>> print cursor.fetchone()
('igor@v2windcenter.com',)

o método fetchone() retornou todos os nomes(foi o que procuramos) que combinem com a condição acima. Aqui não é mais uma tupla de tuplas, e sim uma tuplas de valores, pois nós realmente estamos buscando por um unico valor da uma linha.

Bem,

>>> cursor.close()
>>> con.close()

isso é o básico para se inserir e retirar dados de um banco MySQL.
Para algo mais, visite a pagina no projeto:

http://sourceforge.net/projects/mysql-python