Está en la página 1de 16

Pygame

Lecture Notes
Introducción

Pygame (www.pygame.org) es un módulo adicional de Python que permite crear aplicaciones


multimedia como vídeo juegos. Pygame es basada en la librería SDL y es altamente portable y
compatible con múltiples plataformas como Windows, Linux y MacOS.

En esta oportunidad vamos a revisar cómo utilizar Pygame para crear un vídeo juego sencillo de tipo
Role Playing Game (RPG), al estilo de Legend of Zelda. Para obtener mayores detalles sobre cada uno de
los métodos y funciones de Pygame, se recomienda revisar la documentación oficial disponible en
https://www.pygame.org/docs/genindex.html.

Inicialización y primer dibujo

La Figura 1 muestra el código requerido para dibujar un rectángulo azul de 64x32 pixeles en una
ventana de 800x400 pixeles.

Figura 1. Inicialización, screen y primer dibujo.

Para poder utilizar Pygame, lo primero que debemos hacer es importar el módulo (línea 1). Luego
debemos inicializar el módulo para tener acceso a toda su funcionalidad (línea 3).

Recordemos que Pygame nos permite crear aplicaciones con contenido gráfico, que son diferentes a las
aplicaciones tradicionales que se ejecutan en una consola o línea de comando. Por eso, nuestra nueva
aplicación debe definir una ventana que mostrará todo este contenido gráfico. En nuestro ejemplo, la
línea 4 crea esta ventana con dimensiones 800 pixeles de ancho por 400 pixeles de alto.

La forma más sencilla de “dibujar” algo en Pygame es usar una superficie (Surface). En la línea 6
definimos una nueva superficie de 64x32 pixeles, mientras que la línea 7 pinta, o más precisamente
rellena, la superficie con color azul. Para más detalles de cómo crear otros colores, por favor revisar
https://www.pygame.org/docs/ref/color.html#pygame.Color.
La línea 9 coloca o dibuja la superficie que creamos en la ventana definida anteriormente en la línea 4,
usando la función blit(). Esta función recibe dos parámetros, el primero es la superficie que se va a
dibujar en la ventana y el segundo parámetro es una tupla con la posición en pixeles de la esquina
superior izquierda de la ventana donde se empezará a dibujar la superficie. En nuestro ejemplo, se
colocará la superficie referenciada por la variable heroe a partir de la posición 32,32. Es importante
resaltar que cuando hablamos de coordenadas o posiciones en una ventana de Pygame, la esquina
superior izquierda de la ventana corresponde a las coordenadas 0,0. El primer 0 corresponde a las x y el
otro a las y. Las coordenadas en x crecen hacia la derecha de la ventana, mientras que las coordenadas
en y crecen hacia abajo.

En teoría, en este punto hemos completado nuestro dibujo, excepto que no se visualizan ninguno de
estos cambios porque únicamente los hemos realizado en la memoria de la computadora. Para poder
visualizar los cambios de forma gráfica en la pantalla de nuestra computadora, debemos “actualizar”
nuestra ventana usando el método update, tal como se muestra en la línea 10. Ahora si podemos dar
por concluido el código.

Moviendo mi dibujo

Si se quisiera mover el rectángulo azul que creamos anteriormente a una nueva posición, no basta con
volver a dibujarlo en sus nuevas coordenadas; al contrario, eso crearía un duplicado como se puede
apreciar en la Figura 3.

Figura 2. Dibujando el rectángulo azul en su nueva posición.


Figura 3. Ejecutando el programa de la Figura 2.

Para lograr un verdadero desplazamiento, primero se debería borrar el rectángulo original y luego
dibujar el rectángulo en su nueva posición. Borrar el rectángulo original significa dibujar un rectángulo
de iguales dimensiones en la misma posición pero con el color del fondo (negro en este caso). En la
práctica, si se tendría que hacer eso para cada objeto dibujado en la ventana es más conveniente
redibujar todo el fondo primero (borrar la pantalla) y luego cada uno de los objetos. La Figura 4 muestra
el nuevo código que realiza estas tareas.

Figura 4. Animando al héroe.


Ahora el código empieza creando una superficie de igual dimensión que la ventana rellena de color
negro y dibujada en la ventana en la coordenada 0,0 (ver líneas 7 a 9). Luego, crea y dibuja la superficie
del héroe (líneas 11 a 13). Se actualiza la ventana para mostrar los cambios y se espera 5 segundos antes
de mover el héroe a su nueva posición (líneas 15 y 16). Las últimas cuatro líneas (18 a 21) pinta primero
el fondo en la ventana, luego al héroe en su nueva posición, actualiza la ventana para que se muestren
los cambios y finalmente espera 10 segundos antes de terminar el programa.

Tip para el En realidad el código está colocando una superficie (el héroe) encima de otra (el fondo).
programador Esto es perfectamente normal y posible.

Lo aprendido hasta ahora constituye la base para crear animaciones en Pygame. En juego real, el
movimiento del héroe es controlado por algunas teclas específicas como las flechas direccionales o las
letras A, W, S y D. En la siguiente sección aprenderemos a programar estos eventos.

Programando eventos del teclado

Cuando el usuario aplasta alguna de las teclas del teclado, se genera un evento en nuestro código de
Pygame. Este evento es la forma que tiene la computadora de avisarle a Pygame que ha ocurrido una
acción física con uno de sus dispositivos (teclado, mouse, pantalla táctil, entre otros).

Nuestro trabajo consistirá ahora en capturar ese evento y realizar alguna acción apropiada para el
mismo. Por ejemplo, si se presiona la flecha direccional derecha, moveremos al héroe 50 pixeles a la
derecha. La Figura 5 muestra el código necesario para esto.

Figura 5. Moviendo al héroe con las flechas direccionales.

Las primeras 13 líneas de código no cambian, excepto que ya no es necesario la función sleep() para
detener momentáneamente al programa. En la línea 14 definimos una variable para controlar el valor de
la coordenada x. En la línea 15 iniciamos un lazo infinito conocido como “Lazo Principal del Juego (LPJ)”.
Dentro del LPJ lo único que hacemos por el momento es revisar con un lazo for todos los eventos
generados por la computadora. En cada iteración de este segundo lazo, la variable e contiene el evento
a analizar. La línea 18 plantea una pregunta: si el tipo del evento generado es de tipo
pygame.KEYDOWN (que ocurre cuando se presiona una tecla) y la tecla del evento es
pygame.K_RIGHT, entonces el usuario ha presionado la flecha direccional derecha y debemos realizar
las acciones pertinentes. En este caso, la línea 19 incrementa la coordenada de x en 50 pixeles, mientras
que las tres últimas líneas se encargan de borrar y redibujar todo.

Tip para el Para mayor información sobre los eventos en Pygame, revise
programador https://www.pygame.org/docs/ref/event.html#pygame.event.Event.

Note que nuestro programa ahora no termina nunca pues está atorado en un lazo infinito. Para
solucionar este problema añadiremos un evento más del teclado que capture la tecla “ESC” para
terminar. La Figura 6 muestra las instrucciones que se deben añadir a partir de la línea 17. También se
ha añadido código para terminar el juego si se pulsa la X en la esquina superior derecha de la ventana.

Figura 6. Código para poder terminar la ejecución del programa.

Controlando el borde de la ventana

Otro problema que tiene nuestro código es que el héroe se puede mover hacia la derecha y pasar el
borde de la ventana hasta desaparecer por completo. Esto es fácil de solucionar si controlamos que el
valor de la coordenada x nunca exceda del ancho de la ventana menos el ancho del personaje, 800 y 64
pixeles respectivamente en nuestro ejemplo. Las líneas 25 y 26 del código de la Figura 7 realizan esta
validación.

Figura 7. Verificando que el héroe no sobrepase el borde derecho de la ventana.

Básicamente lo que hacemos es sumar el desplazamiento normal, 50 pixeles en nuestro ejemplo, y


luego preguntar si todo o una parte del rectángulo del héroe excede 800. Note que no basta solo con
preguntar si x es mayor que 800 pues eso verifica si todo el rectángulo del héroe sobrepasa el borde. En
caso de que exceda, le asignamos al héroe una nueva coordenada x = 800 – 64. En otras palabras,
el lado derecho del rectángulo del héroe estará tocando el borde derecho de la ventana pero nunca se
excederá.

Completando el código

A continuación se incluye el código que permite mover al héroe en las cuatro direcciones usando las
flechas del teclado. Nótese que los cuatro bordes de la ventana han sido validados para que el héroe no
pueda cruzarlos.

Figura 8. Código completo de la Primera Parte.


Poniendo la casa en orden

Ahora nuestro código tiene múltiples líneas que dependen de los valores del ancho y altura tanto de la
ventana como del rectángulo del héroe. Estos valores han sido “quemados” en el código lo cual provoca
que un cambio en ellos deba ser realizado en múltiples puntos del código y algo mucho peor, si nos
olvidamos de cambiar en un sitio, nuestro programa no se ejecutará correctamente.

La solución es definir variables y constantes con estos valores y usarlas en todos los puntos donde sea
necesario. Por ejemplo, se puede definir la constante ANCHO_VENTANA = 800 y usar este
identificador en lugar del valor 800 en las líneas 4, 6, 26 y 27. Si luego queremos cambiar el ancho de la
ventana a 750, basta con cambiar la asignación a ANCHO_VENTANA = 750 y todo el resto del código
se ajustará a este nuevo valor. La Figura 9 nos muestra el código con estos cambios.
Figura 9. Código mejorado usando constantes.

Mejorando el playability de nuestro juego.

Playability: The state or property of being playable. A measure of either the ease with which a video
game may be played, or of the overall quality of its gameplay.

Aún cuando nuestro juego es correcto y funcional, podría funcionar mucho mejor. Particularmente, el
jugador no debería necesitar presionar las flechas direccionales todo el tiempo para mover al héroe.
Debería permitirse que si se mantiene presionada una flecha, el héroe se siga moviendo en la misma
dirección hasta que se suelte la flecha o colisione con el borde de la ventana.

Para implementar esta funcionalidad debemos cambiar nuestra filosofía de la solución. Ahora, no
ejecutaremos la acción relacionada con una flecha inmediatamente que dicha flecha es presionada. En
su lugar reconoceremos dos tipos de eventos para una tecla: (1) cuando es presionada y (2) cuando es
liberada. Adicionalmente definiremos cuatro variables booleanas, que representan las cuatro posibles
direcciones, que se activarán cuando su flecha respectiva sea presionada y se desactivarán cuando se
deja de presionar la flecha. Finalmente, las instrucciones que mueven al héroe se ejecutarán solo para
cada una de estas cuatro variables cuyo valor sea True. La Figura 10 muestra el nuevo código.
Figura 10. Mejorando el playability - Parte I.

Luego de ejecutar nuestro código parece que la mecánica del mismo se hubiera dañado. Ahora nuestro
héroe salta de un extremo de la pantalla al otro en lugar de moverse incrementalmente.

Realmente si se está moviendo por incrementos pero lo hace tan rápido que no lo podemos apreciar
hasta que se detiene porque se choca con el borde la ventana. La explicación es que la computadora que
está usando es muy poderosa y rápida de tal manera que está ejecutando el LPJ cientos de veces por
segundo. La solución es tan sencilla como limitar el número de veces que queremos que la computadora
ejecute el LPJ por cada segundo. En la Industria de los vídeo juegos, ese número es 60 veces por
segundo. Para lograr esto usaremos el reloj interno de Pygame como se muestra en el código de las
nuevas líneas 22 y 28 de la Figura 11.

Figura 11. Limitando el número de repeticiones del LPJ.

Finalmente, cambiaremos el valor inicial de la constante DESPLAZAMIENTO en la línea 8 de 50 a 10 (o un


número menor), para lograr un efecto más fluido del movimiento del héroe.
Trabajando con Imágenes (Sprites, Hojas de Sprites, Tiles y Mapas de Tiles)

Una técnica muy común utilizada para construir juegos de vídeo es utilizar conjuntos y secuencias
específicas de imágenes que al mostrarse en cierto orden y configuración, dan la sensación de
movimiento y la estética del juego.

Esta técnica no fue inventada por los creadores de vídeo juegos, al contrario, fue adoptada de un
invento de los 1650s llamado Linterna Mágica (https://es.wikipedia.org/wiki/Linterna_m%C3%A1gica).
Si combinamos la animación con el uso de Tiles y mapas de Tiles, podremos dibujar y animar todo lo
necesario para nuestro juego.

Empecemos por mostrar una imagen.

Mostrando una imagen en Pygame

Mostrar una imagen en Pygame no es muy diferente a mostrar un rectángulo o alguna otra figura
geométrica. Primero debemos empezar por definir el Surface y luego dibujar en esa superficie una
imagen que se obtiene de un archivo. Al momento de dibujar la imagen en la superficie, se debe
especificar la ubicación desde donde se empezará a dibujar la imagen. La Figura 12 enseña el código
para realizar estas tareas.

Figura 12. Mostrando una imagen en Pygame.

La línea 17 crea una superficie con las dimensiones deseadas (32x32 pixeles en este caso.
ANCHO_HEROE y ALTURA_HEROE fueron modificadas para este ejemplo) y “activando” el canal Alpha
o de transparencia. La línea 18 dibuja la imagen contenida en el archivo derecha-0.png empezando en la
coordenada 0,0 de la superficie. Al igual que cuando dibujamos el rectángulo, la imagen se mostrará
solo luego de haber sido añadida a la ventana y de que la misma sea actualizada con
pygame.display.update(). La línea 19 dibuja la superficie heroe en la ventana screen.
Finalmente, la línea 20 actualiza la ventana para que se visualicen los cambios.

Animando un Sprite (Hoja de Sprites)

Un Sprite es simplemente una imagen de un personaje animado de nuestro juego. El uso de Sprites no
es exclusivo al personaje principal. Cualquier “ente” animado de nuestro juego es un Sprite y su
animación es creada usando una hoja de sprite, que no es otra cosa que un archivo con todas las
imágenes que describen todos los posibles movimientos del Sprite. Veamos algunos ejemplos tomados
del Internet.
Normalmente todas las imágenes dentro de una hoja de Sprite tienen las mismas dimensiones pero no
es una regla que se cumple el 100% de las veces. Aun cuando es posible trabajar directamente con la
hoja de Sprite completa y “recortar” con Pygame únicamente las imágenes que se necesitan, en nuestro
ejemplo ya hemos recortado el Sprite en imágenes individuales utilizando herramientas externas de
manipulación de imágenes. En Internet existen múltiples ejemplos de Hojas de Sprites y herramientas
de manipulación de imágenes totalmente gratis y una simple búsqueda retornará varios resultados
relevantes.

El tamaño de las imágenes individuales, en nuestro caso 32x32 pixeles, constituye las dimensiones de la
superficie que se crea para el héroe. Sin embargo, es posible trabajar con imágenes de otras
dimensiones y transformarlas en Pygame al tamaño deseado usando superficie =
pygame.transform.scale(superficie, (nuevo_ancho, nueva_altura)).
Recuerden que este paso es solo necesario si las dimensiones de las imágenes individuales de su Hoja de
Sprite no son las deseadas para el juego.

Ahora, para animar a nuestro personaje, lo que necesitamos hacer es mostrar secuencialmente todas las
imágenes que describen el movimiento del héroe en cada una de sus direcciones (arriba, abajo, derecha
e izquierda) mientras se mantenga presionada la flecha correspondiente. Es importante notar que la
secuencia de animación se repite cuantas veces sea necesaria. En otras palabras, luego de mostrar la
última imagen de la secuencia, regresamos a mostrar la primera y se repete el ciclo.

Para poder implementar esta animación en Pygame, debemos realizar algunas modificaciones a nuestro
código, como se muestra en la Figura 13.

Figura 13. Usando una Hoja de Sprite para animar el héroe a la derecha.
Empezamos por definir una lista en la línea 17 con todas las imágenes del movimiento a la derecha
cargadas con pygame.image.load. Esto ayudará a que nuestra animación se ejecute más rápido porque
cada vez que se carga una imagen se la debe leer de su archivo. Para agilitar este proceso, estamos pre-
cargando todas nuestras imágenes a memoria y cuando sea necesario usarlas, las tomaremos de ahí en
lugar de ir a leer el archivo que la contiene. Las lecturas en memoria son más rápidas que las lecturas en
el disco duro.

En la línea 20 definimos una variable índice idxDerecha que indicará cuál es la última imagen de la
lista que ha sido dibujada. Antes de dibujar la siguiente imagen, idxDerecha se incrementa en uno
considerando que si excede el tamaño de la lista, su valor regresa a cero (0). Esto se muestra en la línea
64.

La líneas 22 y 65 dibujan la imagen ya no a partir de un archivo sino de la lista de imágenes, residente en


memoria, derecha en el índice idxDerecha.

Finalmente, la línea 52 asigna el valor de cero a idxDerecha cuando se suelta la flecha a la derecha.
La imagen en derecha[0] representa la postura neutral del héroe cuando mira a la derecha. La idea
es que al soltar la flecha, el héroe no se quede en la mitad de la animación, por ejemplo con un pie en el
aire.

Si ejecutamos el código podemos observar que el movimiento del héroe hacia la derecha es animado
pero la animación ocurre muy rápidamente y no se perciben los movimientos intermedios. Esto lo
podemos solucionar introduciendo una demora arbitraria repitiendo una imagen un número
preestablecido de veces antes de pasar a la siguiente imagen. Para repetir la imagen vamos a utilizar la
siguiente fórmula para calcular el índice de la imagen a dibujar luego de haber incrementando
idxDerecha en uno:

idxDerecha // FACTOR_HEROE % len(derecha)

Donde FACTOR_HEROE representa la cantidad de veces que quiero repetir una imagen antes de pasar
a la siguiente. La Figura 14 muestra los cambios hechos a las líneas 9, 65 y 66 de nuestro código para
implementar esta funcionalidad.

Figura 14. Añadiendo una demora predeterminada en la animación del héroe.


Si le resulta complicado entender lo que hacen las líneas 65 y 66, piense que esas líneas
Tip para el se ejecutan en un lazo infinito y mediante una corrida de escritorio determine cómo
programador cambia el valor de idxDerecha y el resultado de la fórmula idxDerecha //
FACTOR_HEROE % len(derecha).

Modificando los valores de DESPLAZAMIENTO y FACTOR_HEROE se pueden obtener


Tip para el diferentes efectos de animación del héroe. Los invitamos a experimentar con sus
programador propios valores hasta que encuentren el que se ajuste de mejor manera al tipo de juego
que está implementando.

Finalmente, si repetimos estas ideas para las restantes tres direcciones (arriba, abajo e izquierda),
tendremos un héroe completamente animado. El código final se muestra en la Figura 15.
Figura 15. Animando completamente el movimiento del héroe.

También podría gustarte