Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Pygame Lecture Notes PDF
Pygame Lecture Notes PDF
Lecture Notes
Introducción
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.
La Figura 1 muestra el código requerido para dibujar un rectángulo azul de 64x32 pixeles en una
ventana de 800x400 pixeles.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
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.
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.