Capítulo 18: Detección de colisiones y entradas desde teclado y ratón

Temas Tratados En Este Capítulo:

La detección de colisiones es cuando dos objetos en la pantalla se han tocado (es decir, han colisionado). Por ejemplo, si el jugador toca un enemigo puede perder salud. O quizá el programa necesita saber cuando el jugador toca una moneda para recogerla automáticamente. La detección de colisiones puede ayudar a determinar si el personaje del juego está parado sobre el suelo o si no hay nada más que aire debajo de él.

En nuestros juegos, la detección de colisiones determinará si dos rectángulos se superponen o no. Nuestro próximo programa de ejemplo cubrirá esta técnica básica.

Más adelante en este capítulo, veremos cómo nuestros programas Pygame pueden recibir entradas del usuario a través del teclado o del ratón. Es un poco más complicado que llamar a la función input() como hicimos para nuestros programas de texto. Pero usar el teclado es mucho más interactivo en programas GUI. Y usar el ratón ni siquiera es posible en nuestros juegos de texto. Estos dos conceptos harán que tus juegos sean mucho más emocionantes.

Código del Programa de Detección de Colisiones

Gran parte de este código es similar al programa de animación, de modo que omitiremos la explicación del movimiento y los rebotes. (Ve el programa de animación en el Capítulo 17 para esta explicación). Un rebotín rebotará contra los bordes de la ventana. Una lista de objetos Rect representará cuadrados de comida.

En cada interacción durante el bucle del juego, el programa leerá cada objeto Rect en la lista y dibujará un cuadrado verde en la ventana. Cada cuarenta iteraciones del bucle del juego agregaremos un nuevo objeto Rect a la lista de modo que aparezcan constantemente nuevos cuadrados de comida en la pantalla.

El rebotín es representado por un diccionario. El diccionario tiene una clave llamada ‘rect’ (cuyo valor es un objeto pygame.Rect) y una clave llamada ‘dir’ (cuyo valor es una de las variables constantes de dirección como en el programa de Animación del capítulo anterior).

A medida que el rebotín rebota por la ventana, comprobamos si colisiona con alguno de los cuadrados de comida. Si es así, borramos ese cuadrado de comida de modo que ya no sea dibujado en la pantalla. Esto dará la impresión de que el rebotín “se come” los cuadrados de comida en la ventana.

Escribe lo siguiente en un nuevo archivo y guárdalo como detecciónColisión.py.

El programa se verá como la Figura 18-1. El cuadrado rebotín irá rebotando por toda la pantalla. Al colisionar con los cuadrados de comida verdes estos desaparecerán de la pantalla.

Figura 18-1: Captura de pantalla del programa Detección de Colisiones.

Importando los Módulos

El programa de detección de colisiones importa las mismas cosas que el programa de animación del capítulo anterior, junto con el módulo random.

El Algoritmo de Detección de Colisiones

Para detectar colisiones, necesitas una función que pueda determinar si dos rectángulos se superponen o no. La Figura 18-2 muestra ejemplos de rectángulos superpuestos y no superpuestos.

Figura 18-2: Ejemplos de rectángulos que colisionan (izquierda) y rectángulos que no colisionan (derecha).

La función verifSuperposiciónRects() recibe dos objetos pygame.Rect. La función devuelve True si colisionan y False si no lo hacen. Hay una regla simple a seguir para determinar si los rectángulos colisionan. Mira cada una de las cuatro esquinas de ambos rectángulos. Si al menos una de estas ocho esquinas está dentro del otro rectángulo, quiere decir que los rectángulos han colisionado. Podemos usar esto para determinar si verifSuperposiciónRects() debe devolver
True o False.

Las líneas 5 a 11 comprueban si las esquinas de un rectángulo están dentro del otro. Más tarde, crearemos una función llamada puntoDentroDeRect() que devuelve True si las coordenadas XY del punto está dentro del rectángulo. Llamaremos a esta función para cada una de las ocho esquinas, y si alguna de estas llamadas devuelve True, los operadores or harán que toda la condición sea True.

Los parámetros de verifSuperposiciónRects() son rect1 y rect2. Primero comprueba si las esquinas de rect1 están dentro de rect2, y después si las esquinas de rect2 están dentro de rect1.

No necesitas repetir para rect1 y rect2 el código que comprueba las cuatro esquinas. En cambio, puedes usar a y b en las líneas 7 a 10. El bucle for en la línea 5 usa asignación múltiple. En la primera iteración, a toma el valor rect1 y b toma el valor rect2. En la segunda iteración del bucle, es lo opuesto: a adquiere el valor rect2 y b toma rect1.

Si la línea 11 nunca devuelve True, entonces ninguna de las ocho esquinas comprobadas estan dentro del otro rectángulo. En ese caso, los rectángulos no han colisionado y la línea 13 devuelve False.

Determinando si un Punto está Dentro de un Rectángulo

La función puntoDentroDeRect() es llamada desde verifSuperposiciónRects(). La función puntoDentroDeRect() devolverá True si las coordenadas XY pasadas se encuentran dentro del objeto pygame.Rect pasado como tercer parámetro. De otro modo, esta función devuelve False.

La Figura 18-3 es un ejemplo de un rectángulo y varios puntos. Los puntos y las esquinas del rectángulo están etiquetados con sus coordenadas.

Un punto está dentro del rectángulo si se cumplen las siguientes cuatro afirmaciones:

  • La coordenada X del punto es mayor que la coordenada X del borde izquierdo del rectángulo.
  • La coordenada X del punto es menor que la coordenada X del borde derecho del rectángulo.
  • La coordenada Y del punto es mayor que la coordenada Y del borde inferior del rectángulo.
  • La coordenada Y del punto es menor que la coordenada Y del borde superior del rectángulo.

Si alguna de estas es False, entonces el punto está fuera del rectángulo. La línea 16 combina estas cuatro afirmaciones en la condición de la sentencia if utilizando operadores and.

Figura 18-3: Ejemplo de coordenadas dentro y fuera de un rectángulo. Los puntos (50, 30), (85, 30) y (50, 50) están dentro del rectángulo, el resto están afuera del mismo.

Esta función es llamada desde la función verifSuperposiciónRects() para ver si alguna de las esquinas de los objetos pygame.Rect está dentro del otro. Estas dos funciones te permiten detectar colisiones entre dos rectángulos.

El Objeto pygame.time.Clock Object y el Método tick()

La mayor parte de las líneas 22 a 43 hace lo mismo que hacía el programa de Animación del capítulo anterior: inicializar Pygame, establecer ANCHOVENTANA y ALTOVENTANA, y asignar las constantes de color y dirección.

Sin embargo, la línea 24 es nueva:

En el programa anterior de Animación, una llamada a time.sleep(0.02) reducía la velocidad del programa de modo que no corriese demasiado rápido. El problema con time.sleep() es que puede representar una pausa demasiado larga para computadoras lentas y demasiado corta para computadoras rápidas.

Un objeto pygame.time.Clock puede generar una pausa que sea adecuada para cualquier computadora. La línea 125 llama a relojPrincipal.tick(40)

…dentro del bucle del juego. Esta llamada al método tick() del objeto relojPrincipal calcula la pausa adecuada para que el bucle ejecute unas 40 iteraciones por segundo, sin importar cuál sea la velocidad de la computadora. Esto asegura que el juego nunca se ejecute más rápido de lo esperado. La llamada a tick() debe hacerse sólo una vez en el bucle del juego.

Configurando la Ventana y las Estructuras de Datos

Las líneas 46 a 48 configuran algunas variables para los bloques de comida que aparecen en la pantalla. contadorComida comenzará en el valor 0, NUEVACOMIDA en 40, y TAMAÑOCOMIDA en 20.

La línea 49 configura una nueva estructura de datos llamada rebotín. rebotín es un diccionario con dos claves. La clave ‘rect’ contiene un objeto pygame.Rect que representa el tamaño y la posición del rebotín.

La clave ‘dir’ contiene la dirección en la cual el rebotín se está moviendo. El rebotín se moverá de la misma forma en que se movían los bloques en el programa de animación del Capítulo 17.

El programa lleva un registro de todos los cuadrados de comida con una lista de objetos Rect en comidas. Las líneas 51 y 52 crean veinte cuadrados de comida ubicados aleatoriamente en la pantalla. Puedes usar la función random.randint() para generar coordenadas XY aleatorias.

En la línea 52, llamamos a la función constructor pygame.Rect() para que devuelva un nuevo objeto pygame.Rect. Este objeto representará la posición y el tamaño del cuadrado de comida. Los primeros dos parámetros para pygame.Rect() son las coordenadas XY de la esquina superior izquierda.

Queremos que la coordenada aleatoria esté entre 0 y el tamaño de la ventana menos el tamaño del cuadrado de comida. Si la coordenada aleatoria estuviese simplemente entre 0 y el tamaño de la ventana, el cuadrado de comida podría quedar fuera de la ventana, como en la Figura 18-4.

Figura 18-4: Para un rectángulo de 20 por 20, tener su esquina superior izquierda en (400, 200) en una ventana de 400 por 400 significaría estar fuera de la ventana. Para que el cuadrado esté contenido en de la ventana, la esquina superior izquierda debería estar en (380, 200).

El tercer parámetro de pygame.Rect() es una tupla que contiene el ancho y la altura del cuadrado de comida. Tanto el ancho como la altura corresponden al valor en la constante TAMAÑOCOMIDA.

Dibujando el Rebotín en la Pantalla

Las líneas 71 a 109 hacen que el rebotín se mueva por la ventana y rebote contra los bordes de la misma. Este código es similar a las líneas 44 a 83 del programa de Animación del capítulo anterior, por lo que omitiremos su explicación.

Después de desplazar al rebotín, la línea 112 lo dibuja en su nueva posición. La superficieVentana pasada como primer parámetro indica a Python sobre qué objeto Surface dibujar el rectángulo. La variable BLANCO, que almacena la tupla (255, 255, 255), indica a Python que dibuje un rectángulo blanco. El objeto Rect guardado en el diccionario rebotín en la clave ‘rect’ indica la posición y el tamaño del rectángulo a dibujar.

Colisionando con los Cuadrados de Comida

Antes de dibujar los cuadrados de comida, se comprueba si el rebotín se superpone con alguno de los cuadrados de comida. Si es así, quita ese cuadrado de comida de la lista de comidas. De esta forma, Python no dibujará los cuadrados de comida que el rebotín se halla “comido”.

En cada iteración del bucle for, el cuadrado de comida actual de la lista de comidas (plural) se asigna a la variable comida (singular).

No Agregues o Borres elementos de una Lista mientras Iteras
Sobre Ella

Observe que hay una pequeña diferencia en este bucle for. Si observas detalladamente la línea 115, verás que no está iterando sobre comidas, sino sobre comidas[:].

Recuerda como funcionan las operaciones de rebanado. comidas[:2] se evalúa a una copia de la lista con los ítems desde el principio hasta el ítem en el índice 2 (sin incluir a este último); comidas[3:] evalúa una copia de la lista con los ítems desde el índice 3 y hasta el final de la lista; comidas[:] develve una copia de la lista con todos sus ítems (del primero al último).

Básicamente, comidas[:] crea una nueva lista con una copia de todos los ítems en comidas. Esta es una forma de copiar la lista más corta que, por ejemplo, lo que hace la función obtenerDuplicadoTablero() en el juego de Ta Te Ti.

No puedes agregar o quitar ítems de una lista mientras estás iterando sobre ella. Python puede perder la cuenta de cuál debería ser el próximo valor de la variable comida si el tamaño de la lista comidas está cambiando. Piensa en lo difícil que sería contar el número de caramelos en un frasco mientras alguien está agregando o quitando caramelos. Pero si iteras sobre una copia de la lista (y la copia no cambia mientras lo haces), agregar o quitar ítems de la lista original no será un problema.

Quitando los Cuadrados de Comida

La línea 116 es donde verifSuperposiciónRects() resulta útil. Si el rebotín y el cuadrado de comida se superponen, entonces verifSuperposiciónRects() devuelve True y la línea 117 quita el cuadrado de comida superpuesto de la lista de comidas.

Dibujando los Cuadrados de Comida en la Pantalla

El código en las líneas 120 y 121 es similar a la forma en que dibujamos el cuadrado blanco para el jugador. La línea 120 pasa por cada cuadrado de comida sobre la superficie de superficieVentana. Este programa es similar al programa del rebotín en el capítulo anterior, sólo que ahora el cuadrado que rebota se «come» a los otros cuadrados si pasa por encima de ellos.

Estos últimos programas son interesantes para observar, pero el usuario no puede controlar nada. En el siguiente programa, aprenderemos a obtener entradas desde el teclado.

Código Fuente del Programa de Entradas de Teclado

Crea un archivo nuevo y escribe el siguiente código, luego guárdalo como pygameEntrada.py.

Este programa es idéntico al programa de detección de colisiones. Pero en este programa, el rebotín sólo se mueve mientras el jugador mantiene pulsadas las flechas del teclado.

También puedes hacer clic en cualquier lugar de la ventana y crear nuevos objetos comida. Además, la tecla ESC sale del programa y la “X” teletransporta al jugador a un lugar aleatorio en la pantalla.

Configurando la Ventana y las Estructuras de Datos

Comenzando en la línea 29, el código configura algunas variables que registran el movimiento del rebotín.

Las cuatro variables tiene valores Booleanos para registrar cuáles de las flechas del teclado están siendo pulsadas. Por ejemplo, cuando el usuario pulsa la flecha izquierda en su teclado, se asigna True a moverseIzquierda. Cuando el usuario suelta la tecla, moverseIzquierda vuelve a ser False.

Las líneas 34 a 43 son idénticas al código en los programas Pygame anteriores. Estas líneas gestionan el comienzo del bucle de juego y qué hacer cuando el usuario sale del programa. Omitiremos la explicación de este código que ya ha sido cubierto en el capítulo anterior.

Eventos y Manejo del Evento KEYDOWN

El código para manejar los eventos de tecla pulsada y tecla liberada comienza en la línea 44. Al comienzo del programa, se asigna False a todos estos eventos.

Pygame tiene un tipo de evento llamado KEYDOWN. Este es uno de los otros eventos que Pygame puede generar. La Tabla 18-1 muestra una breve lista de los eventons que pueden ser devueltos por pygame.event.get().

Tabla 18-1: Eventos y cuándo son generados.
Tabla 18-2: Los valores del atributo button y su correspondiente botón del ratón.

Asignando las Cuatro Variables del Teclado

Si el tipo de evento es KEYDOWN, el objeto evento tendrá un atributo key que indica qué tecla ha sido pulsada. La línea 46 compara este atributo con K_LEFT, que es la constante de pygame.locals que representa la flecha izquierda del teclado. Las líneas 49 a 57 realizan comprobaciones similares para cada una de las otras flechas del teclado: K_RIGHT, K_UP, K_DOWN.

Cuando una de estas teclas es pulsada, se asigna True a la variable de movimiento correspondiente. Además, se asigna False a la variable de movimiento en la dirección opuesta.

Por ejemplo, el programa ejecuta las líneas 47 y 48 cuando se pulsa la flecha izquierda. En este caso, se asigna True a moverseIzquierda y False a moverseDerecha (es posible que moverseDerecha ya sea False desde antes, pero le asignamos False sólo para estar seguros).

En la línea 46, evento.key puede ser igual a K_LEFT u ord(‘a’). El valor en evento.key corresponde al valor ordinal entero de la tecla que fue pulsada en el teclado. (No hay valores ordinales para las flechas del teclado, por usamos la variable constante K_LEFT). Puedes usar la función ord() para obtener el valor ordinal de cualquier caracter y compararlo con evento.key.

Al ejecutar el código en las líneas 47 y 48 en caso de que la tecla pulsada haya sido K_LEFT u ord(‘a’), estamos haciendo que la flecha izquierda y la tecla A hagan la misma cosa. El conjunto de teclas W, A, S y D es una alternativa a las flechas del teclado para jugadores que prefieren utilizar la mano izquierda para jugar.

Figura 18-5: Las teclas WASD pueden ser programadas para hacer lo mismo que las flechas del
teclado.

Manipulando el Evento KEYUP

Cuando el usuario libera la tecla que mantenía pulsada, se genera un evento KEYUP.

Si la tecla que el usuario liberó es la tecla ESC, se termina el programa. Recuerda, en Pygame debes llamar a la función pygame.quit() antes de llamar a la función sys.exit().

Las líneas 62 a 69 asignan False a una variable de movimiento si la tecla correspondiente a esa dirección ha sido liberada.

Teletransportando al Jugador

También podemos agregar teletransporte al juego. Si el usuario pulsa la tecla «X«, las líneas 71 y 72 asignarán a la posición del cuadrado del jugador un lugar aleatorio de la ventana. Esto dará al jugador la habilidad de teletransportarse por la ventana pulsando la tecla «X«. Si bien no puede controlar a dónde se teletransportará; es completamente aleatorio.

Manipulando el evento MOUSEBUTTONUP

Las entradas del ratón son manipuladas a través de eventos, igual que las entradas del teclado. El evento MOUSEBUTTONUP ocurre cuando el usuario suelta el botón del ratón después de hacer clic. Se asigna al atributo pos del objeto Event una tupla de dos enteros correspondientes a las coordenadas XY de la posición del ratón en el momento del clic.

En la línea 75, la coordenada X se almacena en evento.pos[0] y la coordenada Y se almacena en evento.pos[1]. La línea 75 crea un nuevo objeto Rect que representa una nueva comida y la ubica donde ha ocurrido el evento MOUSEBUTTONUP. Al agregar un nuevo objeto Rect a la lista de comidas se muestra en la pantalla un nuevo cuadrado de comida.

Desplazando al Jugador por la Pantalla

Hemos asignado a las variables de movimiento (moverseAbajo, moverseArriba, moverseIzquierda y moverseDerecha) True o False dependiendo de qué teclas haya presionado el jugador. Ahora desplazaremos el cuadrado del jugador (representado por el objeto pygame.Rect almacenado en jugador) ajustando las coordenadas XY del jugador.

Si se ha asignado True a moverseAbajo (y el borde inferior del cuadrado del jugador no está por debajo del borde inferior de la pantalla), la línea 88 moverá el cuadrado del jugador hacia abajo agregando VELOCIDADMOVIMIENTO al valor actual del atributo top del jugador. Las líneas 89 a 94 hacen lo mismo para las otras tres direcciones.

El Método colliderect()

En el programa anterior Detección de Colisiones, la función verifSuperposiciónRects() comprobaba si un rectángulo había colisionado con otro. Esta función fue incluida en este libro para que entendieras cómo funciona el código detrás de la detección de colisiones.

En este programa, puedes usar la función de detección de colisiones que viene con Pygame. El método colliderect() de los objetos pygame.Rect recibe como argumento otro objeto pygame.Rect y devuelve True en caso de que los dos rectángulos colisionen y False si no colisionan.

El resto del código es similar al de los programas de Entrada y Detección de Colisiones.

Resumen

Este capítulo presentó el concepto de detección de colisiones, el cual se encuentra en muchos juegos gráficos. Detectar colisiones entre dos rectángulos es fácil: se comprueba si alguna de las esquinas de cada rectángulo está dentro del otro rectángulo. Esta comprobación es tan común que Pygame incluye su propio método de detección de colisiones para objetos pygame.Rect llamado
colliderect().

Los primeros juegos de este libro utilizaban la consola de texto. La salida del programa se escribía en la pantalla y la entrada era texto que el jugador escribía con el teclado. Los programas gráficos, en cambio, pueden aceptar entradas del teclado y del ratón.

Por otra parte, estos programas pueden responder a los eventos generados cuando el jugador pulsa o libera teclas individuales. El usuario no necesita escribir una respuesta completa y pulsar INTRO. Esto permite que el programa responda instantáneamente, lo que resulta en juegos mucho más interactivos.

Listado del programa detección de colisiones

import pygame, sys, random
from pygame.locals import *

def verifSuperposiciónRects(rect1, rect2):
    for a, b in [(rect1, rect2), (rect2, rect1)]:
    # Verifica si las esquinas de a se encuentran dentro de b
        if ((puntoDentroDeRect(a.left, a.top, b)) or
            (puntoDentroDeRect(a.left, a.bottom, b)) or
            (puntoDentroDeRect(a.right, a.top, b)) or
            (puntoDentroDeRect(a.right, a.bottom, b))):
            return True

    return False

def puntoDentroDeRect(x, y, rect):
    if (x > rect.left) and (x < rect.right) and (y > rect.top) and (y < rect.bottom):
        return True
    else:
        return False


# establece el juego
pygame.init()
relojPrincipal = pygame.time.Clock()

# establece la ventana
ANCHOVENTANA = 400
ALTOVENTANA = 400
superficieVentana = pygame.display.set_mode((ANCHOVENTANA, ALTOVENTANA), 0, 32)
pygame.display.set_caption('Deteccion de Colisiones')

# establece las variables de dirección
ABAJOIZQUIERDA = 1
ABAJODERECHA = 3
ARRIBAIZQUIERDA = 7
ARRIBADERECHA = 9

VELOCIDADMOVIMIENTO = 4

# establece los colores
NEGRO = (0, 0, 0)
VERDE = (0, 255, 0)
BLANCO = (255, 255, 255)

# establece las estructuras de datos de comida y rebotín
contadorComida = 0
NUEVACOMIDA = 40
TAMAÑOCOMIDA = 20
rebotín = {'rect':pygame.Rect(300, 100, 50, 50), 'dir':ARRIBAIZQUIERDA}
comidas = []
for i in range(20):
    comidas.append(pygame.Rect(random.randint(0, ANCHOVENTANA - TAMAÑOCOMIDA), random.randint(0, ALTOVENTANA - TAMAÑOCOMIDA), TAMAÑOCOMIDA, TAMAÑOCOMIDA))

# corre el bucle de juego
while True:
    # busca un evento QUIT
    for evento in pygame.event.get():
        if evento.type == QUIT:
            pygame.quit()
            sys.exit()

    contadorComida += 1
    if contadorComida >= NUEVACOMIDA:
        # añade nueva comida
        contadorComida = 0
        comidas.append(pygame.Rect(random.randint(0, ANCHOVENTANA - TAMAÑOCOMIDA), random.randint(0, ALTOVENTANA - TAMAÑOCOMIDA), TAMAÑOCOMIDA, TAMAÑOCOMIDA))

    # Dibuja el fondo NEGRO sobre la superficie
    superficieVentana.fill(NEGRO)

    # Mueve la estructura de datos rebotín
    if rebotín['dir'] == ABAJOIZQUIERDA:
        rebotín['rect'].left -= VELOCIDADMOVIMIENTO
        rebotín['rect'].top += VELOCIDADMOVIMIENTO
    if rebotín['dir'] == ABAJODERECHA:
        rebotín['rect'].left += VELOCIDADMOVIMIENTO
        rebotín['rect'].top += VELOCIDADMOVIMIENTO
    if rebotín['dir'] == ARRIBAIZQUIERDA:
        rebotín['rect'].left -= VELOCIDADMOVIMIENTO
        rebotín['rect'].top -= VELOCIDADMOVIMIENTO
    if rebotín['dir'] == ARRIBADERECHA:
        rebotín['rect'].left += VELOCIDADMOVIMIENTO
        rebotín['rect'].top -= VELOCIDADMOVIMIENTO

    # Verifica si rebotín se movió fuera de la ventana
    if rebotín['rect'].top < 0:
        # rebotín se movió por arriba de la ventana
        if rebotín['dir'] == ARRIBAIZQUIERDA:
            rebotín['dir'] = ABAJOIZQUIERDA
        if rebotín['dir'] == ARRIBADERECHA:
            rebotín['dir'] = ABAJODERECHA
    if rebotín['rect'].bottom > ALTOVENTANA:
        # rebotín se movió por debajo de la ventana
        if rebotín['dir'] == ABAJOIZQUIERDA:
            rebotín['dir'] = ARRIBAIZQUIERDA
        if rebotín['dir'] == ABAJODERECHA:
            rebotín['dir'] = ARRIBADERECHA
    if rebotín['rect'].left < 0:
        # rebotín se movió por la izquierda de la ventana
        if rebotín['dir'] == ABAJOIZQUIERDA:
            rebotín['dir'] = ABAJODERECHA
        if rebotín['dir'] == ARRIBAIZQUIERDA:
            rebotín['dir'] = ARRIBADERECHA
    if rebotín['rect'].right > ANCHOVENTANA:
        # rebotín se movió por la derecha de la ventana
        if rebotín['dir'] == ABAJODERECHA:
            rebotín['dir'] = ABAJOIZQUIERDA
        if rebotín['dir'] == ARRIBADERECHA:
            rebotín['dir'] = ARRIBAIZQUIERDA

    # Dibuja a rebotín en la superficie
    pygame.draw.rect(superficieVentana, BLANCO, rebotín['rect'])

    # Verifica si rebotín intersectó algun cuadrado de comida
    for comida in comidas[:]:
        if verifSuperposiciónRects(rebotín['rect'], comida):
            comidas.remove(comida)
    # Dibuja la comida
    for i in range(len(comidas)):
        pygame.draw.rect(superficieVentana, VERDE, comidas[i])

    # Dibuja la ventana en la pantalla
    pygame.display.update()
    relojPrincipal.tick(40) 

Listado del programa pygameEntrada.py

import pygame, sys, random
from pygame.locals import *

# configurar pygame
pygame.init()
relojPrincipal = pygame.time.Clock()

# configurar ventana
ANCHOVENTANA = 400
ALTURAVENTANA = 400
superficieVentana = pygame.display.set_mode((ANCHOVENTANA, ALTURAVENTANA), 0, 32)
pygame.display.set_caption('Entrada')

# configurar colores
NEGRO = (0, 0, 0)
VERDE = (0, 255, 0)
BLANCO = (255, 255, 255)

# configurar estructuras de datos del jugador y la comida
contadorDeComida = 0
NUEVACOMIDA = 40
TAMAÑOCOMIDA = 20
jugador = pygame.Rect(300, 100, 50, 50)
comidas = []
for i in range(20):
    comidas.append(pygame.Rect(random.randint(0, ANCHOVENTANA - TAMAÑOCOMIDA), random.randint(0, ALTURAVENTANA - TAMAÑOCOMIDA), TAMAÑOCOMIDA, TAMAÑOCOMIDA))

# configurar variables de movimiento
moverseIzquierda = False
moverseDerecha = False
moverseArriba = False
moverseAbajo = False

VELOCIDADMOVIMIENTO = 6


# ejecutar el bucle del juego
while True:
    # comprobar eventos
    for evento in pygame.event.get():
        if evento.type == QUIT:
            pygame.quit()
            sys.exit()
        if evento.type ==KEYDOWN:
            # cambiar las variables del teclado
            if evento.key == K_LEFT or evento.key == ord('a'):
                moverseDerecha = False
                moverseIzquierda = True
            if evento.key == K_RIGHT or evento.key == ord('d'):
                moverseIzquierda = False
                moverseDerecha = True
            if evento.key == K_UP or evento.key == ord('w'):
                moverseAbajo = False
                moverseArriba = True
            if evento.key == K_DOWN or evento.key == ord('s'):
                moverseArriba = False
                moverseAbajo = True
        if evento.type == KEYUP:
            if evento.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            if evento.key == K_LEFT or evento.key == ord('a'):
                moverseIzquierda = False
            if evento.key == K_RIGHT or evento.key == ord('d'):
                moverseDerecha = False
            if evento.key == K_UP or evento.key == ord('w'):
                moverseArriba = False
            if evento.key == K_DOWN or evento.key == ord('s'):
                moverseAbajo = False
            if evento.key == ord('x'):
                jugador.top = random.randint(0, ALTURAVENTANA - jugador.height)
                jugador.left = random.randint(0, ANCHOVENTANA - jugador.width)
        
        if evento.type == MOUSEBUTTONUP:
            comidas.append(pygame.Rect(evento.pos[0], evento.pos[1], TAMAÑOCOMIDA, TAMAÑOCOMIDA))

    contadorDeComida += 1
    if contadorDeComida >= NUEVACOMIDA:
        # agregar nueva comida
        contadorDeComida = 0
        comidas.append(pygame.Rect(random.randint(0, ANCHOVENTANA - TAMAÑOCOMIDA), random.randint(0, ALTURAVENTANA - TAMAÑOCOMIDA), TAMAÑOCOMIDA, TAMAÑOCOMIDA))
    
    # dibujar el fondo negro sobre la superficie
    superficieVentana.fill(NEGRO)

    # mover al jugador
    if moverseAbajo and jugador.bottom < ALTURAVENTANA:
        jugador.top += VELOCIDADMOVIMIENTO
    if moverseArriba and jugador.top > 0:
        jugador.top -= VELOCIDADMOVIMIENTO
    if moverseIzquierda and jugador.left > 0:
        jugador.left -= VELOCIDADMOVIMIENTO
    if moverseDerecha and jugador.right < ANCHOVENTANA:
        jugador.right += VELOCIDADMOVIMIENTO

    # dibujar al jugador sobre la superficie
    pygame.draw.rect(superficieVentana, BLANCO, jugador)

    # comprobar si el jugador ha intersectado algunos de los cuadrados de comida
    for comida in comidas[:]:
        if jugador.colliderect(comida):
            comidas.remove(comida)

    # dibujar la comida
    for i in range(len(comidas)):
        pygame.draw.rect(superficieVentana, VERDE, comidas[i])
    
    # dibujar la ventana sobre la pantalla
    pygame.display.update()
    relojPrincipal.tick(40)