Programar Juegos Arcade
con Python y Pygame

Chapter 5: Introducción a los Gráficos

Bueno, ahora que ya puedes crear bucles, es momento de avanzar en el aprendizaje de cómo crear gráficos. Este capítulo trata de:

5.1 Sistema de Coordenadas de la Computadora

Vídeo: Sistemas de coordenadas para gráficos de ordenador

El sistema de coordenadas Cartesiano mostrado en la figura 5.1(Wikimedia Commons), es el que la mayoría usa a la hora de dibujar gráficas. Es el sistema que nos enseñaron en la escuela. El ordenador usa uno similar, pero distinto. Entender por qué es diferente, requiere conocer un poco de la historia de la computación.

fig.Cartesian_coordinates_2D
Figure 5.1: Sistema de Coordenadas Cartesianas

Durante los primeros 80, la mayoría de sistemas de computación estaban basados en texto, y no permitían trabajar con gráficos. La Figura 5.2(Wikimedia Commons) muestra una hoja de cálculo básica que se ejecutaba en un ordenador Apple ][ muy popular en los 80. Cuando se colocaba texto sobre la pantalla, los programadores empezaban por la parte superior, llamándola línea 1. La pantalla continuaba hacia abajo durante otras 24 líneas y por 40 caracteres a través.

fig.apple3screen
Figure 5.2: Pantalla de texto de un Apple en sus inicios

Aun cuando se usara texto plano, era posible realizar rudimentarios gráficos, tan solo empleando los caracteres del teclado. Mira este gatito de la Figura 5.3 y observa con atención cómo ha sido dibujado. A la hora de hacer este tipo de arte, los caracteres se colocaban empezando desde la línea 1 en la parte superior de la pantalla.

fig.simpleASCII
Figure 5.3: Pantalla de texto

Más tarde, el conjunto de caracteres de amplió para incluir cajas y otras formas básicas de dibujo. Los caracteres se podían escribir en diferentes colores. Tal como se muestra en la Figura 5.4 los dibujos evolucionaron. Si buscas en la web por “ASCII art” encontrarás muchos más ejemplos de todo esto.

fig.spacewar
Figure 5.4: Pantalla de texto espacial

Una vez que los ordenadores evolucionaron para poder controlar individualmente los píxeles para realizar gráficos, el sistema de coordenadas basado en texto se estancó.

fig.Computer_coordinates_2D
Figure 5.5: Sistema de coordenada del ordenador

Las coordenadas $x$ funcionan de la misma forma que en el sistema Cartesiano. Pero las coordenadas $y$ lo hacen al revés. En lugar de que la coordenada cero $y$ se encuentre en la base del gráfico, como en las gráficas Cartesianas, está en la parte superior de la pantalla. A medida que los valores de $y$ aumentan, el sistema de coordenadas del ordenador se mueve hacia abajo, de la misma forma que las líneas de un texto, en lugar de hacerlo como en un gráfico Cartesiano estándar. Ver la Figura 5.5.

Además, se observa que la pantalla muestra el cuadrante inferior derecho del plano Cartesiano, mientras que en el sistema de coordenadas estándar, lo hace habitualmente en el cuadrante superior derecho. Es posible crear objetos en posiciones que tengan coordenadas negativas, pero se dibujarán fuera de pantalla. Esto también puede ser útil. El ordenador se da cuenta de qué es lo que está fuera de pantalla, con lo que el programador no se debe preocupar mucho por ello.

5.2 Biblioteca Pygame

Vídeo: Abriendo una Ventana

Para poder hacer gráficos fácilmente, usaremos Pygame. Pygame es una biblioteca con códigos desarrollados por otras personas que hacen más fácil:

La primera tarea que un programa Pygame necesita hacer es cargar e inicializar la biblioteca Pygame. Todo programa que use Pygame debería empezar con estas líneas:

# Importa  la libraría de funciones llamada 'pygame'
import pygame
# Inicializa el motor de juegos
pygame.init()

Si aún no has instalado Pygame, las instrucciones para hacerlo se encuentran en la sección antes de empezar . Si Pygame no está instalado en el ordenador, obtendrás un mensaje de error al intentar ejecutar import pygame.

No llames “pygame.py” a ningún archivo

Importante: El comando import pygame busca un archivo de biblioteca llamado pygame. Si un programador crea un programa nuevo llamándolo pygame.py, provocará que el ordenador lo importe, en lugar de la biblioteca requerida. Esto impedirá que cualquier programa pueda ejecutarse a menos que borremos ese archivo pygame.py

5.3 Colores

Los siguiente que necesitamos es añadir unas variables que definan los colores que usaremos en nuestro programa. Los colores se definen mediante una lista: rojo, verde y azul (red, green, blue). ¿Has oído alguna vez hablar de un monitor RGB? De ahí viene el término; Red-Green-Blue. Con los antiguos monitores, podías sentarte realmente cerca a él y construir los colores RGB individuales. Al menos hasta que tu madre te llamara y te dijera que no te sentaras demasiado cerca de la tele. Esto es muy difícil hacerlo en la actualidad debido a la alta resolución de los monitores.

Cada elemento de la triada RGB es un número en el rango de 0 a 255. Cero significa que no hay color, y 255 le dice al monitor que muestre todo el color posible. Los colores se combinan de forma aditiva, es decir si los tres son especificados a 255, el color del monitor será blanco.

Las listas en Python puedes estar rodeadas tanto por corchetes como por paréntesis (El Capítulo 7 cubre el tema de listas con detalle y las diferencias entre los dos tipos). En las listas, los números se separan individualmente mediante comas. Debajo hay un ejemplo que crea unas variables y las inicializa mediante listas de tres números. Estas listas las usaremos más adelante para especificar colores

# Definir algunos colores
NEGRO  = (  0,   0,   0)
BLANCO = (255, 255, 255)
VERDE  = (0,   255,   0)
ROJO   = (255,   0,   0)

¿Por qué están en mayúsculas estas variables? Recuerda el Capítulo 1 en que una variable que no cambia se llama constante. No esperamos que el color negro vaya a cambiar; es una constante. Destacamos las constantes por mayúsculas. Si esperamos que un color vaya a cambiar, algo así como color_cielo, el cual cambia a medida que el sol se oculta, entonces, sería una variable en minúsculas.

Usando la shell interactiva de IDLE, intenta definir estas variables e imprimir su salida. Si los colores de arriba no son los que buscas, puedes tú mismo definirlos. Para escoger un color busca en internet un “selector de colores” tal como el que se muestra en la Figura 5.6. Este selector de colores se encuentra en:
http://www.colorpicker.com/

fig.colorpicker
Figure 5.6: Selector de Colores

Extra: Algunos selectores de colores los especifican en hexadecimal. Puedes introducir números hexadecimales si los empiezas con 0x. Por ejemplo:

BLANCO = (0xFF, 0xFF, 0xFF)

Es posible que el programa necesite usar el valor de $\pi$ cuando tenga que dibujar arcos, por lo que será un buen momento para definir una variable que contenga el valor de $\pi$. (También es posible importarlo desde la biblioteca math como math.pi.)

PI = 3.141592653

5.4 Abrir una Ventana

Hasta ahora, los programas que hemos creado solo imprimen texto por pantalla. Estos programas no abren ninguna ventana, tal como lo hacen los programas modernos. El código para abrir una ventana no es complicado. Debajo se puede leer el código necesario, el cual crea una ventana de 700 píxeles de largo por 500 de alto:

dimensiones = (700, 500)
pantalla = pygame.display.set_mode(dimensiones)

¿Por qué set_mode? ¿Por qué no open_window? La razón es que este comando puede hacer muchas más cosas que solo abrir una ventana. También crea juegos que se ejecutan en modo pantalla completa. Esto borra el menú de inicio, el título de las barras y le da al juego, control sobre toda la pantalla. Debido a que esta opción es ligeramente más compleja de usar, y la mayoría de personas prefieren, de todas formas, juegos con ventanas, nos saltaremos las discusión sobre juegos a pantalla completa. Pero si lo que quieres es profundizar sobre juegos a pantalla completa, revisa esta documentación sobre el comando display de pygame .

También, ¿por qué dimensiones = (700, 500) y no dimensiones = 700, 500? Es la misma razón por la que pusimos paréntesis alrededor de las definiciones de los colores. Python, normalmente, no puede almacenar dos números (alto y largo) en una sola variable. La única forma de conseguirlo es si los números se almacenan en una lista. Las listas necesitan paréntesis o corchetes. (Técnicamente, unos paréntesis que rodean a un conjunto de números se llaman una tupla o lista inmutable. Las listas rodeadas de corchetes son, simplemente, listas. Un experimentado programador en Python se revolvería al llamar lista a unos números rodeados por paréntesis, en lugar de llamarla tupla. Las listas se explican con detalle en el Capítulo 7.

Para establecer el título de una ventana (el cual se muestra en la barra de título) usamos la siguiente línea de código:

pygame.display.set_caption("Super Juego del Profesor Craven")

5.5 Interactuando Con el Usuario

Con el código escrito hasta ahora, el programa crearía una ventana que se colgaría inmediatamente. El usuario no podría interactuar con ella, incluso no podría cerrarla. Todo eso necesita ser programado. Necesitamos añadir un código mediante el cual el programa espere en un bucle hasta que el usuario haga click sobre “salida.”

Esta es la parte más complicada del programa y una comprensión completa del mismo no es necesaria todavía. Pero necesitamos una cierta idea de qué es lo que hace. Por ello, dedícale un tiempo a estudiarla y a hacer preguntas.

#Itera hasta que el usuario pincha sobre el botón de cierre.
hecho = False

# Se usa para gestionar cuan rápido se actualiza la pantalla
reloj = pygame.time.Clock()

# -------- Bucle Principal del Programa -----------
while not hecho:
    # TODOS LOS EVENTOS DE PROCESAMIENTO DEBERÍAN IR DEBAJO DE ESTE COMENTARIO
    for evento in pygame.event.get(): # El usuario hizo algo
        if evento.type == pygame.QUIT: # Si el usuario pincha sobre cerrar
            hecho = True # Esto que indica que hemos acabado y sale de este bucle
    # TODOS LOS EVENTOS DE PROCESAMIENTO DEBERÍAN IR ENCIMA DE ESTE COMENTARIO


    # TODA LA LÓGICA DEL JUEGO DEBERÍA IR DEBAJO DE ESTE COMENTARIO

    # TODA LA LÓGICA DEL JUEGO DEBERÍA IR ENCIMA DE ESTE COMENTARIO


    # TODO EL CÓDIGO DE DIBUJO DEBERÍA IR DEBAJO DE ESTE COMENTARIO

    # TODO EL CÓDIGO DE DIBUJO DEBERÍA IR ENCIMA DE ESTE COMENTARIO

    # Limita a 20 fotogramas por segundo (frames per second)
    reloj.tick(20)

Es probable que necesitemos añadir código que maneje las entradas del teclado y ratón. Ese código irá entre los comentarios de los eventos de procesamiento. El código que determina cuándo disparar las balas y el cómo se mueven los objetos irá entre los comentarios de la lógica del juego. Hablaremos de esto en capítulos posteriores. El código de dibujo irá dentro de su respectivo comentario.

5.5.1 Bucle para el Procesamiento de Eventos

Presta Atención!

¡Cuidado! Uno de los problemas más frustrantes que tienen los programadores, es estropear el bucle de procesamiento de eventos. Este código “procesador de eventos” maneja todos las pulsaciones del teclado, los clicks del ratón y algunos otros tipos de eventos. Por ejemplo, tu código podría verse así:

    for evento in pygame.event.get():
        if evento.type == pygame.QUIT:
            print("El usuario solicitó salir.")
        elif evento.type == pygame.KEYDOWN:
            print("El usuario presionó una tecla.")
        elif evento.type == pygame.KEYUP:
            print("El usuario soltó una tecla.")
        elif evento.type == pygame.MOUSEBUTTONDOWN:
            print("El usuario presionó un botón del ratón")

Los eventos (como presionar teclas) van todos en una lista. El programa usa un bucle for para iterar a través de cada evento. Usando una cadena de instrucciones if el código reconoce qué clase de evento ocurrió, y el código para manejarlo va en la declaración if.

Todas las instrucciones if deberían ir juntas en un solo bucle for. Un error común cuando copiamos y pegamos código, no es mezclar bucles de dos programas, sino tener dos bucles de eventos.

    # Este es un bucle de eventos
    for evento in pygame.event.get():
        if evento.type == pygame.QUIT:
            print("El usuario solicitó salir.")
        elif evento.type == pygame.KEYDOWN:
            print("El usuario presionó una tecla.")
        elif evento.type == pygame.KEYUP:
            print("El usuario soltó una tecla.")

    # Aquí el programador ha pegado otro bucle de eventos
    # en el programa. Esto es INCORRECTO. Los eventos ya han sido
    # procesados.    
    for evento in pygame.event.get():
        if evento.type == pygame.QUIT:
            print("El usuario solicitó salir.")
        elif evento.type == pygame.MOUSEBUTTONDOWN:
            print("El usuario presionó un botón del ratón")

El bucle for de la línea 2 ya recogió todos los eventos del usuario. El bucle for en la línea 13 no recogerá nada ya que fueron procesados en el bucle anterior.

Otro problema típico es empezar a dibujar y, entonces, intentar finalizar el bucle de eventos:

    for evento in pygame.event.get():
        if evento.type == pygame.QUIT:
            print("El usuario solicitó salir.")
        elif evento.type == pygame.KEYDOWN:
            print("El usuario presionó una tecla.")


    pygame.rect.draw(pantalla, VERDE, [50,50,100,100])

    # Este es el código que procesa los eventos. Pero no está en el bucle
    # 'for' que procesa los eventos. No trabajará de forma fiable.
    if evento.type == pygame.KEYUP:
        print("El usuario soltó una tecla.")
    elif evento.type == pygame.MOUSEBUTTONDOWN:
        print("El usuario presionó un botón del ratón")

Esto provocará que el programa ignore algunos comandos del teclado y del ratón. ¿Por qué? El bucle for procesa todos los eventos en una lista, por lo que si hay dos teclas que son pulsadas, el bucle for procesará ambas. En el ejemplo anterior, las instrucciones if no se encuentran en el bucle for. Si son múltiples eventos, las instrucciones if solo se ejecutarán para el último evento, en lugar de para todos los eventos.

5.5.2 Procesando Cada Fotograma

La lógica básica y el orden de cada fotograma del juego se sigue por:

El no mezclar estos pasos, hace que el programa sea más fácil de leer y comprender. No escribas algunos cálculos, algunos dibujos, y luego, otra vez, otros cálculos y dibujos. Observa, también, cómo esto se parece a la calculadora que hicimos en el primer capítulo. Obtenemos una entrada del usuario, hacemos los cálculos necesarios y producimos una respuesta. Aquí aplicamos ese mismo patrón.

El código que dibujará la imagen sobre la pantalla reside dentro del bucle while. Con el ciclo de reloj establecido en 10, los contenidos de la ventana se dibujan 10 veces por segundo. Si esto sucede más rápido, el ordenador se ralentiza, ya que todo su tiempo lo dedica a actualizar la pantalla. Si el código estuviera fuera de este bucle, la pantalla no se dibujaría adecuadamente. Si esto sucediera, se mostrarían inicialmente los gráficos, pero éstos no volverían a aparecer, si por ejemplo la minimizamos u otra venta es situada por encima.

5.6 Finalización del Programa

En estos momentos, si pinchamos el botón de “cierre” de la ventana, mientras ejecutamos nuestro programa Pygame dentro del IDLE, conseguiremos que se bloquee. Esto es un inconveniente, ya que cerrar un programa bloqueado requiere de muchos clicks para conseguirlo.

El problema es que aunque hayamos salido del bucle, el programa no le ha dicho al ordenador que cierre la ventana. Llamando al siguiente comando, el programa cerrará cualquier ventana abierta y saldrá de la manera que esperamos.

pygame.quit()

5.7 Limpiando la Pantalla

El siguiente código limpia cualquier cosa que hubiera en la ventana con un fondo blanco. Recuerda que ya definimos anteriormente la variable BLANCO como una lista de 3 valores RGB.

# Limpia la ventana y establece el color del fondo
pantalla.fill(BLANCO)
Vídeo: Representando Líneas

Esto debería hacerse previamente a emitir cualquier comando de dibujo. Limpiar la ventana después de que el programa dibuje los gráficos, consigue que el usuario obtenga una pantalla en blanco.

Cuando se crea por primera vez una ventana, tiene un fondo negro. Es importante, aún, limpiarla, ya que podrían ocurrir una serie de cosas que impedirían que se iniciase adecuadamente. Ningún programa debe asumir que ya posee un lienzo limpio para poder dibujar.

5.8 Actualizar la Pantalla

¡Muy importante! Debes actualizar la pantalla por completo después de dibujar en ella. El ordenador no muestra tus gráficos a medida que los vas dibujando. Esto provocaría que se pusiera a parpadear. Espera a mostrarlos hasta que el programa haya terminado de dibujarlos. El siguiente comando actualiza por completo los gráficos en pantalla.

No incluir este comando significa que el programa solo mostrará una pantalla en blanco. Cualquier código de dibujo posterior a él no aparecerá.

# Avanza y actualiza la pantalla con lo que hemos dibujado.
pygame.display.flip()

5.9 Abre Una Ventana En Blanco

Ahora vamos a poner en un solo programa todo de lo que hemos estado hablando. Este código puede usarse como plantilla general para cualquier programa Pygame. Abre una ventana en blanco y espera a que el usuario presione el botón de cierre.

En la página web, si haces click sobre “Ejemplos” puedes elegir “ejemplos gráficos” y encontrar éste archivo como pygame_base_template.py.

""" 
 Mostramos como usar un sprite respaldado por un gráfico.
 
 Sample Python/Pygame Programs
 Simpson College Computer Science
 http://programarcadegames.com/
 http://simpson.edu/computer-science/

 Vídeo explicativo: http://youtu.be/vRB_983kUMc
"""

import pygame


# Definimos algunos colores
NEGRO = (0, 0 ,0)
BLANCO = (255, 255, 255)
VERDE = (0, 255, 0)
ROJO = (255, 0, 0)
AZUL = (0, 0, 255)
VIOLETA = (98, 0, 255)
 
pygame.init()
  
# Establecemos las dimensiones de la pantalla [largo,altura]
dimensiones = [700,500]
pantalla = pygame.display.set_mode(dimensiones) 
pygame.display.set_caption("Mi Primer juego en Informática")
 
#El bucle se ejecuta hasta que el usuario hace click sobre el botón de cierre.

hecho = False

 
# Se usa para establecer cuan rápido se actualiza la pantalla

reloj = pygame.time.Clock()
 
# -------- Bucle principal del Programa -----------
while not hecho:
    # --- Bucle principal de eventos
    for evento in pygame.event.get():
        if evento.type == pygame.QUIT: 
            hecho = True 
    
    # --- LA LÓGICA DEL JUEGO DEBERÍA IR AQUÍ
 
    # --- EL CÓDIGO DE DIBUJO DEBERÍA IR AQUÍ
    
    # Primero, limpia la pantalla con blanco. No vayas a poner otros comandos de dibujo encima 
    # de esto, de otra forma serán borrados por este comando:
    pantalla.fill(BLANCO)
    
    # --- Avanzamos y actualizamos la pantalla con lo que hemos dibujado.
    pygame.display.flip()

    # --- Limitamos a 60 fotogramas por segundo (frames per second)
    reloj.tick(60)
    
# Cerramos la ventana y salimos.
# Si te olvidas de esta última línea, el programa se 'colgará'
# al salir si lo hemos estado ejecutando desde el IDLE.
pygame.quit()


5.10 Introducción al Dibujo

Esta es una lista de las cosas que puedes dibujar:
http://www.pygame.org/docs/ref/draw.html
Un programa puede dibujar cosas como rectángulos, polígonos, círculos, elipses, arcos y líneas. También incluiremos cómo mostrar texto en los gráficos. La inclusión de imágenes de mapas de bits es tratada en el Capítulo 12. Si decides echar un vistazo a la guía de referencia de pygame, verás la definición de una función como ésta:

pygame.draw.rect(Surface, color, Rect, width=0): return Rect

Una fuente frecuente de confusión es la parte de la línea que dice width=0. Significa que si no provees el grosor, éste por defecto será cero. Así, una llamada de función como:

pygame.draw.rect(pantalla, ROJO, [55, 500, 10, 5])

Será lo mismo que esta otra:

pygame.draw.rect(pantalla, ROJO, [55, 500, 10, 5], 0)

El : return Rect dice que tu función devuelve un rectángulo, lo mismo que ya le hemos pasado. Puedes ignorar por completo esta parte.

Lo que no funcionará es tratar de copiar la línea y poner width=0 dentro.

# Esto falla y el tipo de error que te devuelve el ordenador es 
# realmente difícil de entender
pygame.draw.rect(pantalla, ROJO, [55, 500, 10, 5], width=0)

5.11 Representar Líneas

El siguiente código muestra cómo dibujar una línea sobre la pantalla. La dibujará de color verde, de 5 píxeles de grosor, y desde las coordenadas (0,0) hasta (100,100). Recuerda que VERDE es una variable que ya definimos anteriormente como una lista de 3 valores RGB.

# Dibuja en la pantalla una línea verde desde (0, 0) hasta (100, 100)
# y 5 píxeles de grosor.
pygame.draw.line(pantalla, VERDE, [0, 0], [100, 100], 5)

Usa la plantilla del ejemplo anterior y añade el código para dibujar las líneas. Lee los comentarios para saber exactamente dónde colocar el código. Intenta dibujar líneas con grosores, colores y localizaciones diferentes. Dibuja varias de ellas.

5.12 Representar líneas Mediante Bucles y Desplazamientos

Los programas pueden repetir cosas una y otra vez. El siguiente código dibuja, repetidamente, una línea mediante el uso de un bucle. Los programas pueden usar esta técnica para dibujar múltiples líneas, incluso dibujar un vehículo entero.

Colocar un comando que dibuje una línea dentro de un bucle, causaría que se dibujaran múltiples líneas en la pantalla. Pero, hay un problema. Si cada línea tiene las mismas coordenadas de origen y final, entonces, se dibujarían una encima de la otra. Parecería como si solo hubiéramos dibujado una sola.

Para evitarlo es necesario desplazar las coordenadas cada vez que iteramos el bucle. La primera vez que la variable desplazar_y pasa por el bucle, su valor es cero. La línea se dibuja desde (0,10) hasta (100,110). La siguiente vez, el bucle incrementa la variable desplazar_y en 10. Esto causa que la siguiente línea se dibuje desde las coordenadas (0,20) hasta (100,120). Esto continua una y otra vez en el bucle, desplazando hacia abajo las coordenadas de cada línea en 10 píxeles.

# Dibuja sobre la pantalla varias líneas desde (0, 10) hasta (100, 110)
# de 5 píxeles de grosor usando un bucle while
desplazar_y = 0
while  desplazar_y < 100:
    pygame.draw.line(pantalla, ROJO, [0, 10 + desplazar_y], [100, 110 + desplazar_y], 5)
    desplazar_y = desplazar_y + 10

Este mismo código se podría haber simplificado usando un bucle for:

# Dibuja sobre la pantalla varias líneas desde (0, 10) hasta (100, 110)
# de 5 píxeles de grosor usando un bucle for
for desplazar_y in range(0, 100, 10):
    pygame.draw.line(pantalla,ROJO, [0, 10 + desplazar_y], [100, 110 + desplazar_y], 5)

Ejecuta este código, y prueba a usar diferentes valores para el desplazamiento. Experimenta hasta que entiendas cómo funciona.

Por ejemplo, este es un bucle que emplea el seno y coseno para crear un conjunto de desplazamientos más complejos, produciendo la imagen de la Figura 5.7.

for i in range(200):

	radianes_x = i / 20
	radianes_y = i / 6

	x = int( 75 * math.sin(radianes_x)) + 200
	y = int( 75 * math.cos(radianes_y)) + 200

	pygame.draw.line(pantalla, NEGRO, [x, y], [x + 5, y], 5)
fig.complex_offsets
Figure 5.7: Desplazamientos Complejos

Se pueden dibujar múltiples elementos dentro de un bucle for, tal como el siguiente que dibuja múltiples X's. Figura 5.8.

    for desplazamiento_x in range(30, 300, 30):
        pygame.draw.line(pantalla, NEGRO, [desplazamiento_x, 100], [desplazamiento_x-10, 90], 2 )
        pygame.draw.line(pantalla, NEGRO, [desplazamiento_x, 90], [desplazamiento_x-10, 100], 2 )
fig.multiple_x
Figure 5.8: Múltiples X

5.13 Dibujar un Rectángulo

Vídeo: Cómo representar Rectángulos y Elipses

Cuando dibujamos un rectángulo, el ordenador necesita las coordenadas de la esquina superior izquierda (el origen), así como su altura y longitud.

La Figura 5.9 muestra un rectángulo (y una elipse, lo explicaremos luego) con el origen en (20,20), una longitud de 200 y una altura de 100. Cuando especificamos un rectángulo, el ordenador necesita la lista de estos cuatro números en el orden siguiente: x, y, longitud, altura.

fig.ellipse
Figure 5.9: Representar una Elipse

El ejemplo siguiente dibuja un rectángulo. Los dos primeros números de la lista definen la esquina superior izquierda en el punto (20, 20). Los dos números siguientes indican una longitud de 250 y una altura de 100 píxeles, respectivamente.

El número 2 al final de la línea indica un grosor de 2 píxeles. Cuanto más grande es este número, más gruesa será la línea alrededor de la figura. Si el número fuera 0, no habría borde, sino que todo el rectángulo aparecería relleno por el color especificado.

# Dibuja un rectángulo.
pygame.draw.rect(pantalla, NEGRO, [20, 20, 250, 100], 2)

5.14 Representar una Elipse

Una elipse es representada como si fuera un rectángulo. Especificamos los bordes del rectángulo, y el ordenador dibuja una elipse dentro de ellos.

El error más común al trabajar con elipses, es el pensar que los puntos de partida especifican el centro de la figura. En realidad, nada se dibuja en ese punto. Es la región superior izquierda del rectángulo la que contiene a la elipse.

Volviendo a la Figura 5.9, uno puede ver una elipse de 250 píxeles de largo por 100 píxeles de alto. La esquina superior izquierda del rectángulo de 250 x 100 se encuentra en las coordenadas (20, 20). Observa cómo, realmente, nada ha sido dibujado en (20, 20). Si dibujamos ambas figuras, una encima de la otra, es fácil observar este detalle.

# Representa una elipse, usando un rectángulo como perímetro exterior
pygame.draw.ellipse(pantalla, NEGRO, [20, 20, 250, 100], 2)

5.15 Representar un Arco

Vídeo: Cómo Representar Arcos

¿Qué sucede si el programa solo necesita dibujar parte de una elipse? Esto se consigue con el comando arc. Este comando es similar al comando ellipse , solo que incluye los puntos de partida y final para el arco a representar. Los ángulos están en radianes.

fig.arc
Figure 5.10: Arcos

El siguiente código representa cuatro arcos que muestran los cuatro cuadrantes de un círculo. Cada cuadrante tiene un color distinto para facilitar la visualización de cada sección de arco. El resultado de aplicar este código lo podemos ver en la Figura 5.10.

# Representa un arco como parte de una elipse. Usamos radianes para determinar qué
# ángulo dibujar.
pygame.draw.arc(pantalla, VERDE, [100, 100, 250, 200], pi/2, pi, 2)
pygame.draw.arc(pantalla, NEGRO, [100, 100, 250, 200], 0, pi/2, 2)
pygame.draw.arc(pantalla, ROJO, [100, 100, 250, 200], 3*pi/2, 2*pi, 2)
pygame.draw.arc(pantalla, AZUL, [100, 100, 250, 200], pi, 3*pi/2, 2)

5.16 Representar un Polígono

Vídeo: Cómo Representar Polígonos

La siguiente línea de código representa un polígono. La forma del triángulo se define mediante tres puntos en las posiciones (100, 100), (0, 200) y (200, 200). Es posible hacer una lista con todos los puntos necesarios. Observa cómo se construye cada punto. Cada uno es una lista con dos números, y los tres puntos, a su vez, se anidan en otra lista que los incluye a todos. Este código dibuja lo que podemos observar en la Figura 5.11.

fig.triangle
Figure 5.11: Polígono
# Esto dibuja un triángulo usando el comando polygon
pygame.draw.polygon(pantalla, NEGRO, [[100, 100],[0, 200],[200, 200]], 5)

5.17 Representar Texto

Vídeo: Cómo Representar Texto

Representar texto resulta un poco más complicado. Hay tres cosas que tenemos que hacer en primer lugar. Primero, crear un variable que contenga la información sobre la fuente, tal como el tipo de letra y su tamaño.

Segundo, el programa crea una imagen del texto. Una forma de verlo sería como si el programa tallara un molde con las letras necesarias, listo para ser sumergido en tinta y luego estampado sobre el papel.

Tercero, el programa dice dónde debe 'estamparse' esta imagen sobre la pantalla.

Por ejemplo:

# Selecciona la fuente. Fuente Default, tamaño 25 pt.
fuente = pygame.font.Font(None, 25)

# Reproduce el texto. "True" significa texto suavizado(anti-aliased).
# El color es Negro. Recordemos que ya hemos definido anteriormente la variable NEGRO
# como una lista de (0, 0, 0)
# Observación: Esta línea crea una imagen de las letras,
# Pero aún no la pone sobre la pantalla.
texto = fuente.render("Mi texto", True, NEGRO)

# Coloca la imagen del texto sobre la pantalla en 250 x 250
pantalla.blit(texto, [250, 250])

¿Quieres mostrar en pantalla un número? Pues es un poco más complicado. Lo siguiente no funciona:

texto = fuente.render("Número: ", numero, True, NEGRO)

¿Por qué? Un programa no puede añadir elementos extra a font.render de la misma forma que lo hace print. Solo podemos enviar una cadena de texto al comando, por lo que el valor real de 'numero' necesita añadirse a la cadena “Número: ”. Bueno, pero tampoco esto funciona:

texto = fuente.render("Número: " + numero, True, NEGRO)

Si 'numero' es una variable entera, el comando no sabe cómo sumarle una cadena de texto. Eres tú, el programador, quien debe convertir el 'numero' a cadena. Entonces, sí podremos unir ambas cadenas de esta manera:

texto = fuente.render("Número: " + str(numero), True, NEGRO)

Ahora ya sabes cómo imprimir el 'numero'. Si lo que quieres es imprimir un temporizador, eso requiere formateo de impresión, algo que veremos en un capítulo posterior. Comprueba en el ejemplo de código para timer.py:
ProgramArcadeGames.com/python_examples/f.php?file=timer.py&lang=es

5.18 Programa Completo

Este es el código completo para el programa que hemos tratado en este capítulo. Este programa, así como el resto de ellos, puede ser descargado desde:
http://ProgramArcadeGames.com/index.php?chapter=example_code&lang=es

fig.program_result
Figure 5.12: Resultado del programa ejemplo
""" 
 Sencilla demo gráfica
 
 Sample Python/Pygame Programs
 Simpson College Computer Science
 http://programarcadegames.com/
 http://simpson.edu/computer-science/

"""

# Importamos una biblioteca de funciones llamada 'pygame'
import pygame

# Inicializamos el motor de juegos
pygame.init()

# Definimos algunos colores
NEGRO = (0, 0, 0)
BLANCO = (255, 255, 255)
AZUL = (0, 0, 255)
VERDE = (0, 255, 0)
ROJO = (255, 0, 0)

PI = 3.141592653

# Establecemos el largo y alto de la pantalla
dimensiones = (400, 500)
pantalla = pygame.display.set_mode(dimensiones)

pygame.display.set_caption("Divertido Juego del Profesor Craven")

#Iteramos hasta que el usuario haga click sobre el botón de salida.
hecho = False
reloj = pygame.time.Clock()

# Iteramos en el bucle hasta que hecho == False
while not hecho:
  
    for evento in pygame.event.get(): # El usuario realizó alguna acción
        if evento.type == pygame.QUIT: # Si el usuario hizo click sobre salir
            hecho = True # Marcamos que hemos acabado y abandonamos este bucle

    # Todo el código de dibujo sucede después del bucle for y dentro del bucle
    # while not hecho.
    
    # Limpiamos la pantalla y establecemos su fondo.    
    pantalla.fill(BLANCO)

    # Dibujamos sobre la pantalla una línea verde de 5 píxeles de ancho
    # que vaya desde (0,0) hasta (100,100).    
    pygame.draw.line(pantalla, VERDE, [0, 0], [100, 100], 5)

    # Usando un bucle for, dibujamos sobre la pantalla varias líneas rojas de 5 píxeles 
    # de ancho, que vayan desde (0,10) hasta (100,110).    
    for desplazar_y in range(0, 100, 10):
        pygame.draw.line(pantalla, ROJO, [0, 10 + desplazar_y], [100, 110 + desplazar_y], 5)


    # Dibujamos un rectángulo    
    pygame.draw.rect(pantalla, NEGRO, [20, 20, 250, 100], 2)
    
    # Dibujamos una elipse, usando un rectángulo para fijar sus bordes exteriores    
    pygame.draw.ellipse(pantalla, NEGRO, [20, 20, 250, 100], 2) 

    # Dibujamos un arco como parte de una elipse. 
    # Usamos radianes para determinar qué ángulo tenemos que dibujar.    
    pygame.draw.arc(pantalla, NEGRO, [20, 220, 250, 200], 0, PI / 2, 2)
    pygame.draw.arc(pantalla, VERDE, [20, 220, 250, 200], PI / 2, PI, 2)
    pygame.draw.arc(pantalla, AZUL, [20, 220, 250, 200], PI, 3 * PI / 2, 2)
    pygame.draw.arc(pantalla, ROJO, [20, 220, 250, 200], 3 * PI / 2, 2 * PI, 2)
    
    # Aquí dibujamos un triángulo empleando el comando polygon.    
    pygame.draw.polygon(pantalla, NEGRO, [[100, 100], [0, 200], [200, 200]], 5)

    # Elegimos que tipo de fuente usar; fuente por defecto, y de 25 puntos.    
    fuente = pygame.font.Font(None, 25)

    # Creamos el texto. "True" significa texto con antialiasing. 
    # El color es negro. Esto nos crea una imagen de las letras, pero no las coloca 
    # sobre la pantalla.    
    texto = fuente.render("Mi texto", True, NEGRO)

    # Coloca la imagen del texto en pantalla sobre las coordenadas 250x250.    
    pantalla.blit(texto, [250, 250])

    # Avanzamos y actualizamos la pantalla con lo que hemos dibujado.
    # Esto DEBE suceder después del resto de comandos de dibujo.    
    pygame.display.flip()

    # Aquí limitamos el bucle while a un máximo de 60 veces por segundo.
    #Lo dejamos aquí y usamos toda la CPU que podamos.    
    reloj.tick(60)
    

# Pórtate bien con el IDLE
pygame.quit()

5.19 Repaso

5.19.1 Test

Haz click para ir al Test.

5.19.2 Ejercicios

Haz click para ir a los Ejercicios.

5.19.3 Taller

Haz click para ir al Taller.


You are not logged in. Log in here and track your progress.