PyGame

Creando un fondo con scroll

Posted on mayo 16, 2007. Filed under: PyGame, Python |

Es una característica típica de los viejos juegos de naves, hay un tutorial en ingles muy bueno aquí, pero para este post hice algo diferente y mucho mas fácil de entender.

El codigo del ejemplo lo pueden descargar de aquí (se llama scroll.zip)

Hago una pequeña explicación, primero creamos la clase Fondo pero con 2 images, solo sirve para que puedan ver el efecto scroll con un fondo diferente

class Fondo(pygame.sprite.Sprite):   
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect=load_image(‘fondo.jpg’,0)
        self.image2, self.rect2 = load_image(‘fondo2.jpg’,0)

En la función main() vemos el código clásico de cualquier juego en pygame, definimos la pantalla, creamos un fondo y un objeto de tipo clock que nos servirá para fijar los cuadros por segundos, en este caso lo fijaremos a 30 escribiendo clock.tick(30) dentro del while.

desplazamiento = 0
imagen_de_fondo=imagen.imagen2
while 1:
   
        for event in pygame.event.get():
            if event.type == QUIT:
                raise SystemExit
            if  event.type == MOUSEBUTTONDOWN:
                imagen_de_fondo=fondo.image
            elif event.type is MOUSEBUTTONUP:
                imagen_de_fondo=fondo.image2
               

        desplazamiento +=2
        if desplazamiento==64: desplazamiento=0
        screen.blit(imagen_de_fondo,(0,0), (desplazamiento,0,HEIGHT,WIDTH))
        pygame.display.flip()
        clock.tick(30)

Hay un par de cosas para remarcar:

  • Al clickear el mouse intercambiamos el fondo
  • Hay una variable desplazamiento que va desde 0 a 64 (de 2 en 2) eso hace que en cada cuadro desplacemos el fondo 2 pixels.

La función blit de este ejemplo copia la imagen de fondo en la superficie screen, pero en este caso le pasamos 2 parametros nuevos, uno sirve para posicionarlo en (0,0) es decir (no estoy seguro) en la esquina superior izquierda y con el 2do parámetro le enviamos un rectángulo de la imagen fondo, pero desplazado.
Las imagenes de fondo tienen 124 pixeles mas de ancho que la pantalla, si no hiciéramos eso no podríamos lograr el efecto de desplazamiento.

Powered by ScribeFire.

Anuncios
Leer entrada completa | Make a Comment ( 6 so far )

La clase sprite

Posted on mayo 15, 2007. Filed under: PyGame, Python |

Leyendo esta excelente guia de ejemplos de pygame, quiero hablar un poco de la clase sprite.

La clase sprite puede ser usada como una clase base para diferentes objetos, y contiene algunas funciones muy útiles para trabajar con grupos de sprites. Por ejemplo podemos tener el grupo de las naves y el grupo de los asteroides y con funciones como groupcollide, controlar cuales naves chocaron con los asteroides. A continuación un ejemplo sencillo de creación de grupos de sprites:

naves=pygame.sprite.Group()
xwing = Xwing()
naves.add(xwing())

asteroides=pygame.sprite.Group()
asteroides.add(Clase_asteroides(200,300))

En el código anterior, creamos un primer grupo de sprites llamado naves, y usando la funcion add vamos agregandole sprites. La linea que dice xwing=Xwing() sirve para crear un objeto de tipo Xwing (que es un sprite).
Luego de esto creamos otro grupo asteroides y agregamos en el grupo de sprites un objeto, pero en este caso lo instanciamos dentro de la función.

Hay 3 grandes tipos de grupos:

  • GroupSingle: solo permite un sprite, si agregamos otro, el que estaba se borraria
  • Group: un grupo de sprites común y corriente
  • RenderUpdates: un grupo que tiene la funcion draw modificada, ya que produce una salida.

Es decir que los grupos tipo Group y RenderUpdates cuando se hace draw() es como si hicieramos un blit() de todos las imagenes que hay en cada sprite, solo que en RenderUpdates la función draw() tiene una respuesta, veamos un ejemplo (modificado de la guia que dejé al principio)

all_sprites=pygame.sprite.Group()       #noten que en el ejemplo se usa RenderPlain, pero esta en desuso
all_sprites.add(player)                           #agregamos el objeto player al grupo all_sprites
all_sprites.add(alien)                             #agregamos el objeto alien

while 1:
       all_sprites.update()                          #actualizamos todos los sprites
       screen.fill((200,200,200))                #pintamos la superfie screen con el color gris
       all_sprites.draw(screen)                  #hacemos un blit en la superficie screen de TODOS los sprites del grupo all_sprites
       pygame.display.flip()                        #mostramos todo lo que hay en el “reverso” de la página

all_sprites.empty()                                   #cuando salimos del while borramos todos los sprites del grupo

Como se puede observar, en cada ciclo del programa se actualizan los sprites, se pinta el fondo de la pantalla (screen) y se dibujan (con la función draw) TODOS los sprites, ya sea que hayan cambiado o no. Esta técnica tiene la desventaja de tener que dibujar TODO siempre, en la actualidad las máquinas son muy rápidas y no tendrían problemas, pero puede ser que el juego tenga una cierta complejidad que no convenga dibujar todo sino una región y en este caso conviene usar otra cosa.

Hay una alternativa que mejoraría el juego y es utilizando el grupo de sprites RenderUpdates, veamos un ejemplo:

all_sprites = pygame.sprite.RenderUpdates()            #creamos un grupo all_sprites
all_sprites.add(player)                                                   #agregamos player al grupo

while 1:
       all_sprites.update()                                                  #actualizamos el grupo
       all_sprites.clear(screen, background)                  #en la superficie screen, pintamos con el background solo en las posiciones de los sprites del grupo.
       pygame.display.update(all_sprite.draw(screen))
all_sprites.empty()

Hay un par de cosas que debemos aclarar de este código. La primer parte es igual solo que creamos un grupo de tipo RenderUpdates. Dentro del while actualizamos los sprites, luego en vez de borrar toda la pantalla (como hacíamos antes pintando de gris todo el fondo), aquí usando la función clear() del grupo, y con esto “pintaremos” solo en aquellas partes donde haya un sprite del grupo all_sprites, la sintaxis es la siguiente:

Group.clear(superficie_destino, superficie_origen) #pintaremos con la superficie_origen solo en los sprites de la superficie_destino.

A continuación de esto hacemos un display.update y un all_sprite.draw, por que? En realidad es muy simple: como había dicho antes la función draw de la clase RenderUpdates esta modificada y devuelve un listado de rectángulos que fueron modificados, es decir devuelve las posiciones de pantalla que cambiaron de un cuadro a otro, esta respuesta le pasamos como parámetro a display.update() que se diferencia de display.flip() por el hecho de que solo actualiza algunas porciones de pantalla.

Al final haremos que nuestro juego de “navecitas” tenga una mejor velocidad de cuadros por segundos, ya que en cada ciclo no se actualizará toda la pantalla sino solo las partes que se necesiten. Hay veces que esta segunda técnica puede ser contraproducente y es cuando hay tantas pero tantas cosas en la pantalla, que directamente conviene dibujarla entera que estar calculando y dibujando porciones.

Quedaría ver otro tipo de grupo, que es OrderedUpdates, este grupo es una modificacion de RenderUpdates por el hecho de que dibuja los sprites que contiene de acuerdo al orden en que fueron siendo agregados y por eso, borrar y agregar sprites es un poco mas lento.

Para mas información visitar la documentación oficial de esta clase.

Powered by ScribeFire.

Leer entrada completa | Make a Comment ( 2 so far )

Visor de Fotos

Posted on mayo 14, 2007. Filed under: PyGame, Python |

Estuve probando funciones para transformar fotos, lectura de eventos, arrays, clases, etc y realice un sencillo visor de fotos basándome en 2 códigos que encontré por ahí (un ejemplo de arrastrar y soltar de loserjuegos y otro de un lector de imagenes dentro de un zip.)

Es muy simple, se puede aumentar de tamaño a las fotos, rotarlas y moverlas, con solo manejar el mouse. Pueden descargarlo desde aqui, recuerden que deben tener Numpy ya que utiliza arrays (para instalar Numpy pueden leer el post anterior)

Cualquier parecido con esto es pura coincidencia :p

Powered by ScribeFire.

Leer entrada completa | Make a Comment ( None so far )

Arrays en Pygame

Posted on mayo 13, 2007. Filed under: Array, PyGame, Python |

Para trabajar con arrays hay que tener el paquete Numpy (antes conocido como Numeric) de python, ya que  pygame no lo trae incluido así que hay que instalarlo.
Lo bajan de aqui y pueden elegir el .exe o el codigo fuente, para eso hay que escribir lo siguiente: python setup.py install

La libreria Numpy tienen millones de funciones utiles para realizar calculos matematicos, tiene desde arccos() hasta funciones para ordenar por quicksort, son muchisimas, en esta pagina hay un listado con ejemplos.

Para utilizar esta libreria, al comienzo de nuestro codigo fuente hay que agregar esto: from numpy import * entonces nuestra cabecera quedaria asi:

import pygame
from pygame.locals import *
from
numpy import *

Para el caso puntual de la creación de un vector:

a =  array ([1,2,3,4])
b= array([5,6,7,8])
print a + b

Resultado: [6,8,10,12]

Hay muchos ejemplos de trabajos con arrays, lo mejor seria que chequeen los ejemplos que trae en la documentación, en el link que les pasé antes.

Algunas funciones importantes:

Por ejemplo para crear un vector vacio (lleno de ceros) usamos la funcion zero, de esta forma:

zeros(5)   #devuelve un vector con 5 ceros (en flotante)

zeros ((2,2), int)  #devuelve:
                                                  [0,0]
                                                  [0,0]

Atencion: si no se especifica int, la funcion zeros devuelve un array con coma flotante.

Otra función bastante útil es crear un array con numeros aleatorios, para eso usamos la función random_integers(), pero para esto debemos agregar el paquete random de numpy y nuestro código quedaría así:

from numpy import *
from
numpy.random  import *

Entonces para crear un array con números aleatorios escribimos lo siguiente random_integers(minimo,maximo,tamaño) donde mínimo es el menor numero aleatorio posible y máximo el mayor, en tamaño debemos establecer la dimensión del array, por ejemplo puede ser (2,2) o  (3,5), etc.

Si quisiéramos obtener un numero aleatorio y no un array usamos la función randint()

Una aplicación práctica

Hoy necesitaba guardar los botones que se están presionando en el mouse, entonces utilicé la funcion get_pressed pero esta función solo me devolvía los 3 botones presionados (y mi mouse tiene 5 contando la ruedita) ejemplo de uso:

botones=pygame.mouse.get_pressed()  #esto me devuele un array de 3 elementos

Si yo tengo apretado el boton izquierdo me devuelve este array (1,0,0) y si tengo apretado el derecho (0,0,1). La desventaja de esta función, es que no lee la “ruedita del mouse”, entonces utilice lo siguiente:

botones=zeros(5,int)       #creamos un vector (0,0,0,0,0) para guardar los estados de los botones

while 1:
       for event in pygame.event.get():     
          if event.type == MOUSEBUTTONDOWN:
             botones[event.button-1]=1
          if event.type == MOUSEBUTTONUP:
             botones[event.button-1]=1

Con este codigo estamos leyendo siempre si se aprietan o se sueltan los botones del mouse. Si giramos la ruedita del mouse puede ser para arriba (boton 4) o para abajo (boton 5), y en la posicion 3 y 4 respectivamente guardamos 1. Todo esto lo solucionamos de una manera bastante sencilla usando arrays.

Nota: En este ejemplo, cuando giramos la ruedita, se graba 1 en el vector pero como luego la soltamos se vuelve a poner en cero, y parece que no esta apretado, para solucionar eso podemos usar una variable bandera.

Powered by ScribeFire.

Leer entrada completa | Make a Comment ( 1 so far )

Nociones fundamentales de pygame

Posted on mayo 12, 2007. Filed under: PyGame, Python |

Cuando empecé con pygame lo que me mas me costó era entender sobre que era un superficie (surface) un sprite, como mostrar las imagenes, cual era la mejor forma, que es el doblebuffer de la pantalla, etc. Por eso me propongo con este post aclarar estos términos que me costaron entender. No pretendo que sea una guia para comenzar con la programación de video-juegos, solo aclarar algunas ideas un tanto complicadas.

Superficie y explicación de la función blit

En pygame toda imagen se representa mediante una superficie y esto permite combinarlas.
Por ejemplo tenemos 2 superficies una llamada screen_sup y otra llamada fondo_sup, la superficie screen_sup abarca toda la ventana (por ahora esta vacia, no tiene imagen dentro), y la superficie fondo_sup es una imagen jpg.

screen_sup = pygame.display.get_surface() 
fondo_sup = pygame.image.load(“archivo.jpg”)

Usando la funcion blit copiamos y pegamos la superficie fondo_sup en la posicion x,y de la superficie screen_sup

screen_sup.blit(fondo_sup, (x,y)) # funcionamiento superficie_destino.blit(superficie_origen,(coordenadas))

Las superficies tienen diferentes funciones, algunas muy utiles, por ejemplo fill que pinta la superficie de un color, o get_width y get_height que devuelven el ancho y el alto respectivamente de la superficie. Uso:

alto=fondo_sup.get_height()
ancho=fondo_sup.get_width()
rectangulo=fondo_sup.get_rect() #devuelve las coordenadas de un rectángulo que cubre la superficie

Para ver un listado de todos los posibles metodos que tienen las superficies pueden visitar este enlace.

Doblebuffer y explicación de la función flip

Cuando mostramos una imagen y la queremos mover de lugar, tenemos que borrarla, moverla un poco, mostrarla, moverla un poco mas, borrarla, etc hasta llegar al destino, pero a veces este proceso es muy rápido y el monitor no llega a dibujar por completo cada imagen del desplazamiento y parece como si la imagen estuviese entrecortada, para evitar tener que estar dibujando, borrando dibujando usaremos el doblebuffer.

El doblebuffer son 2 areas de memoria ram, donde se guardan las imagenes, una de las areas siempre estará activa, es decir sera visible para el usuario y la otra estará oculta. Veamos un ejemplo:

Imagen de la pelota en la posicion 1
while posicion_pelota<=10
    mover_pelota_una_posicion()
    pygame.display.flip()

Cuando movemos nuestra pelota no la veremos hasta que no hayamos ejecutado pygame.display.flip(), en el primer paso del programa la imagen que se ve es la de la posicion 1 y la imagen que esta oculta es la de la posicion 2, pero al escribir pygame.display.flip() intercambiamos las pantallas y mostraremos la posicion 2 y ocultaremos la 1. Cada cambio de posicion se hace de forma oculta y hasta que no hacemos flip no veremos el efecto de movimiento.

Por se explica que en el ejemplo de screen_sup y fondo_sup si no escribimos pygame.display.flip() al final, no veremos el fondo.

El usar display.flip() hace que se cambie toda pantalla completa y esto puede ser una desventaja, pero hay técnicas que permiten intercambiar solo las regiones que hayan sido modificadas esta ultima técnica a veces da mejores resultados en cuanto a velocidad en los movimientos.

Sprite

habíamos dicho que una superficie es cualquier imagen que usamos en pygame, ya que para cargar cualquier imagen usamos pygame.load.image(archivo) y esta función solo devuelve una superficie.
En el caso de los sprites es mas general, la clase de los sprites se usa para diferentes tipos de objetos, por ejemplo un objeto sprite puede contener a una superficie y métodos o funciones.

A continuación creamos una clase sprite llamada Auto, esta clase tiene una imagen (superficie) correspondiente y tiene 3 métodos: encender, frenar y update.

class Auto(pygame.sprite.Sprite)
   
    def __init__(self,foto):
       pygame.sprite.Sprite.__init_(self)
       self.velocidad=0
       self.imagen = pygame.load.image(foto)
   
    def frenar(self):
       self.velocidad=0

    def encender(self, angulo):
       self.velocidad=2
      
    def update(self):
       self.rect.move_ip(self.velocidad,0)

Para instanciar las clases hacemos:

fiat= Auto(“fiat.jpg”)
ferrari = Auto (“f1.jpg”)

Lo que hicimos con esto es crear 2 objetos auto, cada uno con diferentes imagenes. No voy a ampliar esto por que merece que un parrafo aparte. Quiero comentar lo que son los grupos.

Una gran utilidad cuando tenemos muchos autos, naves, enemigos, etc es crear grupos, por ejemplo el grupo_de_enemigos

grupo_enemigos = pygame.sprite.Group()   #creamos el grupo de los enemigos
grupo_enemigos.add(Enemigo(imagen_enemigo1)) #agregamos un enemigo
grupo_enemigos.add(Enemigo(imagen_enemigo2)) #agregamos otro enemigo

Con esto creamos un grupo y le agregamos 2 enemigos, la ventaja principal de los grupos es poder “enviarle ordenes” a todos los miembros, las ordenes mas importantes son update y draw, con update, actualizamos todos sprites (en el caso de que se hayan movido, cambiado de tamaño, etc) y con draw estamos haciendo un blit de la superficie que esta incluida en cada sprite (al comienzo habiamos dicho que los sprite pueden contener superficies y antes habiamos hablado de que la funcion blit sirve para pegar una superficie en otra), entonces si escribimos

grupo_enemigos.draw(screen_sup)

Es como si hicieramos un blit por cada superficie que esta incluida en el grupo de sprites

screen_sup.blit(imagen_enemigo1)
screen_sup.blit(imagen_enemigo2)
….

Hay que recordar que al final de todo debemos escribir pygame.display.flip() como ya habíamos explicado.

Espero que haya aclarado algunas dudas, por lo menos ahora no estoy tan perdido. Seria recomendable que lean “Conceptos Básicos para el desarrollo de videojuegos”  y esta “Introduccion a Pygame

Muchos de los conceptos de este post fueron tomados de allí.

Powered by ScribeFire.

Leer entrada completa | Make a Comment ( 1 so far )

Mostrar Ventana con Pygame

Posted on mayo 7, 2007. Filed under: PyGame |

Asi creamos una ventana en Pygame

screen = pygame.display.set_mode((600, 400))
pygame.display.set_caption(‘Titulo del Juego’)
screen = pygame.display.get_surface()

La primera linea indica el ancho y el alto. La segunda coloca el titulo de la ventana. La Tercera Hace que esta ventana sirva para mostrar todas las imágenes.

En este momento, cuando corremos el programa, lo que se ve es que dura unas milésimas la ventana y luego desaparece. Para solucionar esto lo que hacemos es crear un Loop o bucle infinito. Pero hay que tener cuidado con lo que ejecuta este bucle, para que no bloque el sistema.

El otro problema, es que así como esta el programa, este no se cierra con la X. Para esto hay que crear una función que compruebe que se a pulsado la X de cerrar. El problema esta en que esta función toca ejecutarla en todo momento, para que siempre este alerta en que momento se pulsa la X.

Lo que tenemos que hacer con esta función es ejecutarla en un bucle infinito. Y entonces de esta manera solucionamos dos problemas de una. Hacemos que el Bucle infinito, que se necesita para mantener la ventana abierta, ejecute la función que detecta los eventos de mouse teclado, y entre ellos, el evento se pulsar la X.

La función la hacemos de la siguiente manera.

def detectarEventos(eventos):
for evento in eventos:
if evento.type == QUIT:
sys.exit(0)

Lo que hace es crear un for que revisa todo el contenido que tenga el paramentro que coloquemos. El parametro que mas adelanta vamos a colocar allí sera pygame.event.get(). este es una función, que creo que contienete todos los tipos de eventos. Si alguno de ellos es de tipo QUIT ejecuta la función sys.exit(0).

El bucle seria asi.

while True:
detectarEventos(pygame.event.get())

Y ya esta. Bastante mas difícil que Flash, pero eso es lo que cuesta ser plataforma. Todo el código quedaría así.

import pygame, sys,os
from pygame.locals import *
pygame.init()

screen = pygame.display.set_mode((600, 400))
pygame.display.set_caption(‘Titulo del Juego’)
screen = pygame.display.get_surface()

def detectarEventos(eventos):
for evento in eventos:
if evento.type == QUIT:
sys.exit(0)
while True:
detectarEventos(pygame.event.get())

Leer entrada completa | Make a Comment ( 7 so far )

Iniciar o inicializar PyGame

Posted on mayo 7, 2007. Filed under: PyGame, Python |

Iniciamos PyGame con estas 3 Lineas

import sys, os,  pygame
from pygame.locals import *
pygame.init()

import sys,os,
Esta linea importa los modulos de Python y del Sistema. Esto permite que el archivo se ejecute independientemente del sistema

pygame
Aca importa todos los modulos de Pygame

from pygame.locals import *
Aca importamos un modulo especial de Pygame. Los miembros de este modulo son las constantes y las funciones de uso general de Pygame, como Rect que permite crear un rectángulo entre otras funciones.

pygame.init()
Con esta función inicia todos los módulos de Pygame

Leer entrada completa | Make a Comment ( 5 so far )

Liked it here?
Why not try sites on the blogroll...