Tutorial: Escribiendo juegos en Python I | Manzana Mecánica


Tutorial: Escribiendo juegos en Python I | Manzana Mecánica

Muchos de nosotros gozamos a diario jugando videojuegos. Y somos muchos los que siempre hemos querido hacer nuestro propio juego. Cuando yo era chico programé infinidad de juegos en mi Atari. Pero programar un juego no es cosa sencilla, y aprender el lenguaje C no es para cualquiera.

Existe una plataforma de código libre para escribir juegos de manera sencilla, en un lenguaje poderoso. Esta es la primera parte de una serie de artículos que escribiré para enseñar a utilizar las librerías pygame para hacer videojuegos. Estas librerías son open-source y están disponibles para las plataformas tradicionales (Linux, MacOSX y Windows).

¿Qué es pygame?

Pygame es un conjunto de librerías para el lenguaje Python que facilitan el escribir juegos. Hacen fáciles tareas complicadas como cargar imágenes, desplegarlas en la pantalla, almacenar y tocar sonidos/música; también te permiten abstraerte de problemas como qué tipo de hardware tienen los usuarios.

Para aprender a usar pygame sólo necesitas tener conocimientos de Python. Como el enfoque de este tutorial no es aprender Python, les dejo la página oficial con tutoriales sobre cómo aprender este lenguaje.

Instalar pygame

La manera más sencilla de instalar Pygame es hacerlo en Linux :D, con un simple comando en el terminal. En Ubuntu la instalación es así:

  1. sudo apt-get install python-pygame

Se siguen las instrucciones y listo. En la página de Pygame pueden encontrar instrucciones para instalarlo en Windows y MacOSX

Manos a la obra

Lo primero que haremos será crear un archivo de texto llamado “demo1.py”, y copiar estas líneas en él. Luego les explicaré qué significan.

  1. import pygame
  2. from pygame.locals import  *
  3. SCREEN_WIDTH = 800
  4. SCREEN_HEIGHT = 600
  5. def game():
  6.         pygame.init()
  7.         screen = pygame.display.set_mode( (SCREEN_WIDTH,SCREEN_HEIGHT) )
  8.         pygame.display.set_caption( “demo1” )
  9.    
  10.         while True:
  11.                 keyinput = pygame.key.get_pressed()
  12.                 if keyinput[K_ESCAPE] or pygame.event.peek(QUIT):
  13.                         raise SystemExit
  14. if __name__ == ‘__main__’:
  15.         game()

Vamos por partes. Con las siguientes líneas lo que hacemos es decir que usaremos las librerías:

  1. import pygame
  2. from pygame.locals import *

Luego definimos un par de constantes, como la resolución de pantalla que usaremos para nuestro juego:

  1. SCREEN_WIDTH = 800
  2. SCREEN_HEIGHT = 600

Así, nuestro juego correrá en 800×600. Luego escribimos una función llamada “Game”. Veamos el cuerpo de la función. Lo primero que hacemos es inicializar los sistemas de pygame:

  1.         pygame.init()

Después le decimos a pygame la resolución que usaremos y el nombre de nuestro juego. Como lo corremos (por ahora) dentro de una ventana, este nombre es el que se desplegará en el título de la ventana.

  1.         screen = pygame.display.set_mode( (SCREEN_WIDTH,SCREEN_HEIGHT) )
  2.         pygame.display.set_caption( “demo1” )

Finalmente, entramos a lo que es el ciclo principal del juego. Este ciclo tomará todo el tiempo de juego. Es en este juego en que sucede la inteligencia de los personajes, se calculan las nuevas posiciones, se dibujan los gráficos y se toca la música. Demos un vistazo.

Comenzamos el ciclo:

  1.         while True:

Ahora le pedimos a Pygame que nos dé un arreglo con todas las teclas que están siendo presionadas en este instante. Y luego vemos si la tecla ESCAPE está presionada. Si esto es así, procedemos a terminar el juego. Para terminar, basta con salir del ciclo principal.

  1.                 keyinput = pygame.key.get_pressed()
  2.                 if keyinput[K_ESCAPE] or pygame.event.peek(QUIT):
  3.                         raise SystemExit

Y por último, estas líneas sirven para que el juego comience. Son líneas típicas de los programas en Python, así que no las explicaremos.

  1. if __name__ == ‘__main__’:
  2.         game()

Corriendo nuestro juego

Ha llegado la hora correr el juego. Para eso, simplemente ejecuta este comando en un terminal abierto en la misma carpeta de tu juego:

  1. python demo1.py

¿Y qué veremos? Pues muy poco hasta aquí. Simplemente veremos una ventana en negro con el título de nuestro juego. Si presionas la tecla ESCAPE la ventana se cerrará.

No podemos decir que nuestro juego es muy interesante hasta ahora, pero es la base de todos los programas que podrás escribir de ahora en adelante.

Agregando una imagen de fondo

Vamos un poco más allá de lo básico. Agregar una imagen de fondo a nuestro juego es muy sencillo. Primero debemos crear una función que es la que usaremos para leer imágenes desde el disco duro.

  1. def load_image(filename, transparent = False):
  2.         try: image = pygame.image.load(filename)
  3.         except pygame.error, message:
  4.                 raise SystemExit, message
  5.         image = image.convert()
  6.         if transparent:
  7.                 color = image.get_at((0,0))
  8.                 image.set_colorkey(color, RLEACCEL)
  9.         return image

Esta función es bastante simple. Lo que hace es leer la imagen desde el disco duro y almacenarla en una variable temporal. Si el segundo parámetro es verdadero, entonces la función get_at se usa para obtener el color del pixel en la posición (0,0). Esto lo haremos cuando trabajemos con personajes. Por ahora puedes ignorarlo.

Luego, descarga esta imagen y ponla en la misma carpeta que el juego.

Ahora, los últimos toques al código. Haremos algunas modificaciones al método principal. Antes del ciclo while agregaremos las siguientes líneas:

  1. screen = pygame.display.set_mode( (SCREEN_WIDTH,SCREEN_HEIGHT) )
  2. background_image = load_image(‘back.jpg’);

Lo que hicimos aquí fue crear una superficie. Las superficies son los objetos gráficos de Pygame. Pero esta superficie en particular es la que representa a la ventana principal del juego. Lo que dibujemos en ésta saldrá dibujado en nuestro juego. Luego cargamos la imagen “back.jpg” en memoria y la almacenamos en la variable background_image.

Ahora agregamos bajo el ciclo while estas otras líneas:

  1. screen.blit(background_image, (0,0) )
  2. pygame.display.flip()

Estas últimas dos líneas hacen que por cada ciclo la imagen de fondo se dibuje sobre la superficie de la pantalla. Los parámetros (0, 0) son para indicarle al intérprete que la esquina superior izquierda de la imagen se dibuje sobre la esquina superior izquierda de la pantalla. En otras palabras, es la posición en la pantalla.

Resultado final

El código final de esta segunda versión sólo es un poco más largo. Veamos:

  1. import pygame
  2. from pygame.locals import *
  3. SCREEN_WIDTH = 800
  4. SCREEN_HEIGHT = 600
  5. def load_image(filename, transparent = False):
  6.         try: image = pygame.image.load(filename)
  7.         except pygame.error, message:
  8.                 raise SystemExit, message
  9.         image = image.convert()
  10.         if transparent:
  11.                 color = image.get_at((0,0))
  12.                 image.set_colorkey(color, RLEACCEL)
  13.         return image
  14. def game():
  15.         pygame.init()
  16.         screen = pygame.display.set_mode( (SCREEN_WIDTH,SCREEN_HEIGHT) )
  17.         pygame.display.set_caption( “demo1” )
  18.         background_image = load_image(‘back.jpg’);
  19.         while True:
  20.                 screen.blit(background_image, (0,0) )
  21.                 pygame.display.flip()
  22.                 keyinput = pygame.key.get_pressed()
  23.                 keyinput = pygame.key.get_pressed()
  24.                 if keyinput[K_ESCAPE] or pygame.event.peek(QUIT):
  25.                         raise SystemExit
  26.  
  27.        
  28. if __name__ == ‘__main__’:
  29.         game()

Si lo hiciste todo bien, tipeando “python demo1.py” deberías ver algo como esto:

Bueno, con esto terminamos la primera parte. En la segunda veremos cómo hacer personajes y lograr que éstos se muevan por la pantalla. ¡Nos vemos!

Bueno, ha pasado un poco más de un mes, pero lo prometido es deuda. Les dejo aquí la segunda parte de nuestro tutorial de juegos en Python y pygame. Si te perdiste la primera parte, no te preocupes, sigue este enlace.

En esta segunda parte comenzaremos a implementar uno de los juegos más clásicos de la historia de los videojuegos: Arkanoid.

Después de esto, sabrás como hacer que un objeto se mueva por tu pantalla y una manera simple de agregar sonidos a tus juegos. Si ya estás ansioso de empezar a programar, entonces sigue leyendo

La clase Ball

En la parte anterior del tutorial dejamos el código listo para empezar lo más entretenido. Lo que llevamos hasta ahora inicializa el sistema y luego muestra el fondo que utilizaremos.

Ahora vamos a empezar definiendo la clase Ball que sera la que se encargue de representar a la bola del juego. Esta primera versión de la bola rebotará indistintamente en las cuatro murallas de nuestra pantalla. Veamos primero el constructor de la clase

  1. class Ball(pygame.sprite.Sprite):
  2.      def __init__(self):
  3.          pygame.sprite.Sprite.__init__(self)
  4.          self.image = load_image(‘ball.gif’, True)
  5.          self.rect = self.image.get_rect()
  6.          print self.rect
  7.          self.rect.centerx = SCREEN_WIDTH / 2
  8.          self.rect.centery = SCREEN_HEIGHT / 2
  9.          self.speed = [0.5, –0.5]

Veamos ahora el código línea por línea. Lo primero que notamos es que nuestra clase hereda de la clase Sprite de pygame. Esta clase representa los objetos gráficos que se moverán por nuestra pantalla. En la siguiente línea llamamos al constructor de la clase Sprite. Esto es MUY IMPORTANTE, pues si no lo haces, al ejecutar el programa saldrán errores muy raros y difíciles de entender si no sabes qué es lo que pasa.

Luego incializaremos algunas variables. Primero cargamos al imagen de la bola desde el archivo ball.gif (puedes descargarlo desde aquí). Para ello usamos nuestra función load_image que definimos en el tutorial anterior. Toda imagen leída con pygame tiene una referencia a un rectángulo. Este rectángulo es del ancho y alto de la imagen y lo utilizaremos para posicionarlo en el mundo del juego. Así pues, lo primero que hacemos es guardar una referencia a ese rectángulo usando el método get_rect de la clase Image. Por definición los rectángulos de las imágenes aparecen con su esquina inferior derecha en la coordenada (0,0). Lo primero que hacemos es trasladarlo al centro de la pantalla modificando el punto del centro (siguientes dos líneas). El centro delrectángulo puede ser modificado usando la propiedad center (un punto) o a través de las coordenadas del centro, centerx y centery.

Vale la pena aquí hacer ver lo versátil de la clase Rect (los rectángulos). Cada vez que modificas una de sus propiedades, el resto cambiará para adaptarse. Por ejemplo, si el lado izquierdo de unrectángulo es 50 y el derecho es 100 (un rectángulo de 50 pixeles de ancho) al hacer rect.centerx = 100 entonces ahora el lado izquierdo valdrá 75 y el derecho valdrá 125 (lo trasladamos 25 pixeles a la derecha). Esto es lo que usaremos para mover nuestros objetos por la pantalla.

Y en la última línea inicializaremos la velocidad de la pelota. La velocidad la manejaremos de manera separada para cada coordenada X e Y. Esto simplificará mucho nuestros cálculos.

Bien, veamos ahora el siguiente método de Ball.

  1.     def update(self, time):
  2.          self.rect.centerx += self.speed[0] * time;
  3.          self.rect.centery += self.speed[1] * time;
  4.          if self.rect.left <= 0 or self.rect.right >= SCREEN_WIDTH:
  5.              self.speed[0] = –self.speed[0]
  6.              self.rect.centerx += self.speed[0] * time;
  7.          if self.rect.top <= 0 or self.rect.bottom >= SCREEN_HEIGHT:
  8.              self.speed[1] = –self.speed[1]
  9.              self.rect.centery += self.speed[1] * time;

El método update será llamado en cada ciclo del juego. Su misión es actualizar las variables internas de la bola. Aquí es donde le asignamos “inteligencia” a nuestra pelota. El parámetro time representa el tiempo (en milisegundos) que ha pasado en nuestro juego desde la última vez que esta función fue ejecutada.

Lo primero es actualizar la posición de la bola. Como nos enseñaron en el colegio, si multiplicas la velocidad de un objeto por el tiempo que lleva moviéndose, obtendrás la posición del objeto después de ese tiempo. Así, lo primero será actualizar la coordenada x del centro aumentándola en su velocidad (self.speed[0]) multiplicada por el tiempo que ha pasado (time). Esto dejará esa coordenada donde debería encontrarse luego de viajar time milisegundos a self.speed[0] unidades. Luego hacemos lo mismo para la coordenada y.

Si dejáramos este método hasta aquí, nuestra pelota comenzaría a moverse pero muy pronto saldría de nuestra pantalla. Para evitar esto las siguientes líneas hacen el truco. Si luego de actualizar nuestra posición nos damos cuenta de que nos hemos pasado fuera de la pantalla entonces lo que hacemos es cambiar el sentido de la velocidad en esa coordenada y luego volver a sumarlo al centro (esto hace que nos “devolvamos” un poco).

SI YA HAZ LEÍDO HASTA AQUÍ, NO DESESPERES!!! FALTA POCO

Bien, ya solo nos falta utilizar todo lo que hemos escrito. Lo que haremos ahora es crear una bola y hacer que esta se mueva por la pantalla. Para esto, usaremos la clase Group de pygame. Un group es un “grupo” de sprites. Es muy parecido a las listas de de python pero tiene un par de “gracias” que los hacen muy útiles para nuestros propósitos. La idea de los groups es agrupar objetos gráficos parecidos en propiedades para manipularlos de manera conjunta. Veamos ahora el caso mas sencillo, un grupo con un solo objeto. En nuestro programa principal escribiremos

  1.       balls = pygame.sprite.Group()
  2.       ballSprite = Ball()
  3.       balls.add(ballSprite)

Con esto hemos creado el grupo balls y le hemos agregado una nueva bola. Podríamos agregar cuantas bolas quisieramos, pero como todas aparecen en el centro, se superpondrian y no veríamos diferencias. Un buen ejercicio es modificar el constructor de Ball para que reciba las coordenadas de inicio y luego agregar más de una bola al grupo.

Ahora introduciremos otra clase muy útil y sencilla de usar: Clock. Esto es todo lo que nos faltará para animar nuestra bola. Un clock es un “reloj”, osea, un objeto que nos sirve para medir intervalos de tiempo. Cada vez que invocamos su método tick() este nos dirá cuanto tiempo ha pasado desde la vez anterior a la invocación. Así, lo primero que haremos en cada ciclo del programa principal es llamar a tick para ver cuánto tiempo ha pasado.

El último párrafo se resume en estas 3 líneas

  1.       clock = pygame.time.Clock()
  2.       while True:
  3.               time = clock.tick()

Inmediatamente después de esto, es el momento de que nuestros objetos se actualisen, es decir, que “usen su inteligencia”. Para hacerlo más fácil, hacemos lo siguiente.

  1. balls.update(time)

El método update de Group recorre a todos los sprites del grupo y ejecutará el método update del sprite correspondiente pasándole como parámetro time que es el tiempo desde la actualización anterior.

Y finalmente, dibujaremos todos nuestros objetos, es decir, el fondo y la bola. Primero el fondo, pues si lo hiciéramos al revés entonces el fondo pisaría a la bola.

  1.               screen.blit(background_image, (0,0) )
  2.               balls.draw(screen);

La primera línea la conocemos de la primera parte del tutorial y en la segunda hay un poco de truco. Draw es otro de esos métodos que recorren los sprites del grupo. Pero esta vez no ejecutamos un método del sprite, sino que tomamos la imagen de este y la dibujaremos usando como referencia su rectángulo. Es por esta razón que todo objeto que hereda de Sprite debe tener siempre llenas las variables rect e image pues el método draw espera que estos tengan valores correctos.

Y listo!!! Corriendo el juego

Ahora ya estamos listos para correr nuestra primera versión del juego con movimiento. Para que no tengan que escribir todo lo anterior, les dejo aquí el código completo.

  1. import pygame
  2. from pygame.locals import *
  3.  
  4. SCREEN_WIDTH = 800
  5. SCREEN_HEIGHT = 600
  6.  
  7.  
  8. class Ball(pygame.sprite.Sprite):
  9.      def __init__(self):
  10.          pygame.sprite.Sprite.__init__(self)
  11.          self.image = load_image(‘ball.gif’, True)
  12.          self.rect = self.image.get_rect()
  13.          self.rect.centerx = SCREEN_WIDTH / 2
  14.          self.rect.centery = SCREEN_HEIGHT / 2
  15.          self.speed = [0.5, –0.5]
  16.  
  17.      def update(self, time):
  18.          self.rect.centerx += self.speed[0] * time;
  19.          self.rect.centery += self.speed[1] * time;
  20.          if self.rect.left <= 0 or self.rect.right >= SCREEN_WIDTH:
  21.              self.speed[0] = –self.speed[0]
  22.              self.rect.centerx += self.speed[0] * time;
  23.          if self.rect.top <= 0 or self.rect.bottom >= SCREEN_HEIGHT:
  24.              self.speed[1] = –self.speed[1]
  25.              self.rect.centery += self.speed[1] * time;
  26.    
  27.  
  28.  
  29. def load_image(filename, transparent = False):
  30.       try: image = pygame.image.load(filename)
  31.       except pygame.error, message:
  32.               raise SystemExit, message
  33.       image = image.convert()
  34.       if transparent:
  35.               color = image.get_at((0,0))
  36.               image.set_colorkey(color, RLEACCEL)
  37.       return image
  38. def game():
  39.       pygame.init()
  40.       screen = pygame.display.set_mode( (SCREEN_WIDTH,SCREEN_HEIGHT) )
  41.       pygame.display.set_caption( “demo1” )
  42.       background_image = load_image(‘back.jpg’);
  43.  
  44.       balls = pygame.sprite.Group()
  45.       ballSprite = Ball()
  46.       balls.add(ballSprite)
  47.       clock = pygame.time.Clock()
  48.       while True:
  49.               time = clock.tick()
  50.               balls.update(time)
  51. if __name__ == ‘__main__’:
  52.       game()

Si todo salió correctamente entonces deberías ver una pelota rebotando contra los bordes de la pantalla.

Un bonus: Agregando sonidos

Vamos a adelantarnos en nuestros tutoriales y aprender la manera más básica de agregar sonidos a nuestro juego. Para eso, pueden descargar este archivo de sonido y ponerlo en el directorio del juego. Luego, en el constructor de la clase bola de la siguiente manera.

  1.          self.ping = pygame.mixer.Sound(‘ping.wav’)

Esto carga el sonido en memoria y guarda una referencia para ser usada luego. Ahora, en el método update, busquen en donde se cambia la dirección de la velocidad. Este es el momento en que la bola choca y por lo tanto el momento de hacer que el sonido suene. Agreguen las siguientes lineas

  1.          self.ping.play()

Y cuando la pelota golpee contra los muros el sonido se escuchará.

Y después de esto… que más

Bueno, pues viene mucho más. No hemos hecho más que empezar. En la siguiente parte aprenderemos como mover objetos usando el mouse y como detectar coliciones entre objetos. Esto es más complicado así que merece un capítulo aparte.

Los espero en la siguiente parte del tutorial

Blogged with the Flock Browser
Anuncios

Publicado el 29 marzo 2009 en 1ro ES-CB, 2do ES- CB, Programación, Pygame, Python y etiquetado en , , . Guarda el enlace permanente. 4 comentarios.

  1. es excenle pude entenderlo casi a la perfeccion ya q recien estoy llevando pygame.python y tambien soy novato.
    pero aun asi gracias por el codigo lo estudiare mas

  2. al tratar de instalar pygame escribo en el terminal esto: sudo apt-get install python-pygame y me sale lo siguiente:
    [root@servgese ~]# sudo apt-get install python-pygame
    sudo: apt-get: command not found
    no se porque ya que soy novata en el asunto por fa me podrias decir que hacer…
    GRACIAS !!!

  3. Buenas!

    Felicidades por este tutorial que estas haciendo. Llegué aquí buscando la forma de como crear el GUI para pequeños scripts que estoy creando.

    Empecé en python con un e-book (Invent your own computer games with Python by Al Sweigart) donde te enseña el código básico creando pequeños juegos. Y cuando llegue aquí me vino en la mente este manual.

    Basta de hablar y vamos a por el tema:
    En la segunda parte del tutorial al código completo te has dejado alguna cosilla al final:

    screen.blit(background_image, (0,0) )
    balls.draw(screen);

    A parte de todo esto, que solo llevo unos meses y con mucha calma con el Python, tengo algunos problemas. El codigo de la bola en movimiento que he hecho siguiendo un poco tu tutorial es este (si me equivoco, corrígeme):

    import pygame
    from pygame.locals import *

    SCREEN_WIDTH = 800
    SCREEN_HEIGHT = 600

    class Ball(pygame.sprite.Sprite):

    def __init__(self):

    pygame.sprite.Sprite.__init__(self)

    self.image = load_image(‘ball.gif’, True)
    self.rect = self.image.get_rect()

    print self.rect

    self.rect.centerx = SCREEN_WIDTH / 2
    self.rect.centery = SCREEN_HEIGHT / 2
    self.speed = [0.5, -0.5]

    def update(self, time):

    self.rect.centerx += self.speed[0] * time;
    self.rect.centery += self.speed[1] * time;

    if self.rect.left = SCREEN_WIDTH:
    self.speed[0] = -self.speed[0]
    self.rect.centerx += self.speed[0] * time;

    if self.rect.top = SCREEN_HEIGHT:
    self.speed[1] = -self.speed[1]
    self.rect.centery += self.speed[1] * time;

    def load_image(filename, transparent = False):

    try: image = pygame.image.load(filename)

    except pygame.error, message:
    raise SystemExit, message

    image = image.convert()

    if transparent:
    color = image.get_at((0,0))
    image.set_colorkey(color, RLEACCEL)

    return image

    def game():

    pygame.init()

    screen = pygame.display.set_mode( (SCREEN_WIDTH,SCREEN_HEIGHT) )

    pygame.display.set_caption( “demo2” )

    background_image = load_image(‘back.jpg’);

    balls = pygame.sprite.Group()
    ballSprite = Ball()
    balls.add(ballSprite)

    clock = pygame.time.Clock()

    while True:
    screen.blit(background_image, (0,0) )
    balls.draw(screen);
    time = clock.tick()
    balls.update(time)

    if __name__ == ‘__main__’:
    game()

    Al momento de ejecutarlo sale la ventana de color negro y no aparece ni el fondo ni la bola, igualmente seguiré estudiando el código haber si encuentro algo.

    Saludos cordiales!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: