Paredes y colisiones en nuestro clon de Tetris

Vamos a dar más cuerpo a nuestro clon de Tetris, añadiendo los siguientes elementos:

  • Paredes del juego, bien visibles en la pantalla, para saber en todo momento dónde están los límites
  • Colisiones de las piezas con esas paredes. Además, si la colisión es desde arriba, la pieza queda fija en su posición y da paso a la siguiente.
  • Cuando pulsemos la flecha hacia abajo y mientras no pulsemos otra tecla, la pieza baja más rápido.

Con esto, habríamos cubierto estos puntos del plan que, realmente, cubren la parte más difícil de toda la programación:

  1. Dibujar una pieza (básica) y programar la rotación: lo vimos hace tres semanas
  2. Programar la gravedad y el resto de movimientos: lo vimos hace dos semanas
  3. Colisiones de la pieza con otras piezas y el borde de la pantalla. El artículo de hoy
  4. Haciendo líneas y retocando la mecánica anterior
  5. Puntuaciones y niveles
  6. Borde, marcador, tabla de puntuaciones. Con este artículo cubriremos también el borde, entendiendo borde como un marco visible
  7. Refinando los gráficos de las piezas

Una vez lo tengamos hecho y para poder jugar, sólo tendremos que programar la mecánica de las líneas. El resto de artículos añaden ese “acabado” que hace posible competir entre varios jugadores, y algo de estética o retoques de código. Dentro de poco, en cualquier caso, habremos superado lo realmente importante del juego y podremos jugar partidas “infinitas”.

Os recomiendo qué debéis leer si llegáis a PItando en este momento, para poder aprovechar este artículo:

  1. La serie de Python, en general.
  2. La serie de Pygame, en particular.

¡Vamos a por ello!

Programa de partida

Partiremos del resultado del último artículo, que puedes copiar desde este cuadro (que tendrás que expandir):

import pygame

class Pieza:
    'Modela una pieza de Cuatris :)'
    def __init__(self, matriz, color, fondo, pantalla, lado_cuadrado):
        self.matriz = matriz
        self.color = color
        self.fondo = fondo
        self.pantalla = pantalla
        self.lado_cuadrado = lado_cuadrado
        # posición de la pieza
        self.x = 0
        self.y = 0
        self.calcula_dimensiones()

    def calcula_dimensiones(self):
        self.filas = len(self.matriz)
        self.columnas = len(self.matriz[0])
        
    def rotar90(self):
        'Rota la pieza en el sentido horario'
        self.matriz = list(zip(*self.matriz[::-1]))
        self.calcula_dimensiones()
        
    def rotarM90(self):
        'Rota la pieza en el sentido antihorario'
        self.matriz = list(zip(*self.matriz))[::-1]
        self.calcula_dimensiones()

    def __pinta_cuadrado(self, x, y, color):
        'dibuja un cuadrado en las coordenadas de malla especificadas'
        cuadrado = (x*self.lado_cuadrado,
                    y*self.lado_cuadrado,
                    self.lado_cuadrado,
                    self.lado_cuadrado)
        cuadrado_interno = (x*self.lado_cuadrado+8,
                            y*self.lado_cuadrado+8,
                            self.lado_cuadrado-16,
                            self.lado_cuadrado-16)
        pygame.draw.rect(self.pantalla,
                         (int(color[0]/2),
                          int(color[1]/2),
                          int(color[2]/2)),
                         cuadrado,
                         0)
        pygame.draw.rect(self.pantalla, color, cuadrado_interno, 0)

    def __borra_rectangulo(self, rectangulo):
        'borra el área especificada'
        pygame.draw.rect(self.pantalla, self.fondo, rectangulo, 0)

    def pinta(self, x_ini, y_ini):
        'pinta la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        # actualizamos la posición de la pieza
        self.x = x_ini
        self.y = y_ini
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] == 1:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, self.color)
                    
    def borra(self, x_ini, y_ini):
        'borra la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        rectangulo_pieza = (x_ini*self.lado_cuadrado,
                          y_ini*self.lado_cuadrado,
                          (x_ini+self.columnas)*self.lado_cuadrado,
                          (y_ini+self.filas)*self.lado_cuadrado)
        self.__borra_rectangulo(rectangulo_pieza)

    # Movimiento de la pieza
    def mover(self, desplazamiento_x, desplazamiento_y):
        'mueve la pieza el desplazamiento indicado (sin pintarla), en coordenadas de malla'
        self.x = self.x + desplazamiento_x
        self.y = self.y + desplazamiento_y

    # Funciones que hacen uso del estado de la pieza
    def auto_pinta(self):
        'pintar la pieza en las propiedades autocontenidas'
        self.pinta(self.x, self.y)

    def auto_borra(self):
        'borrar la piezaden las propiedades autocontenidas'
        self.borra(self.x, self.y)


BLANCO = (255,255,255)
NEGRO = (0, 0, 0)
MAGENTA = (200,0,200)
FPS = 60
SLOT = 40

# pantalla 10 x 20 y posición inicial de la pieza
ANCHO = 10*SLOT
ALTO = 20*SLOT
x_ini = 4
y_ini = 0

# inicializamos pygame
pygame.init()

# definición de la pantalla
pantalla = pygame.display.set_mode((ANCHO, ALTO))
pantalla.fill(NEGRO)

matriz_L = [(1, 0),
            (1, 0),
            (1, 1)]

L = Pieza (matriz_L, MAGENTA, NEGRO, pantalla, SLOT)
L.pinta(x_ini, y_ini)

pygame.display.update()

# reloj de control de refresco
clock = pygame.time.Clock()

# Evento de gravedad
GRAVEDAD = pygame.USEREVENT + 1
t_GRAVEDAD = 2000 #milisegundos
pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)

continuar = True
while continuar: 
    # pausa hasta el siguiente "tick" de reloj
    clock.tick(FPS)

    # detección de evento QUIT (aspa)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            continuar = False
        else:
            # Procesar la gravedad
            if event.type == GRAVEDAD:
                L.auto_borra()
                L.mover(0, 1)
                L.auto_pinta()
     
            # Movimiento
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_z:
                    L.auto_borra()
                    L.rotarM90()
                    L.auto_pinta()

                if event.key == pygame.K_x:
                    L.auto_borra()
                    L.rotar90()
                    L.auto_pinta()

                if event.key == pygame.K_LEFT:
                    L.auto_borra()
                    L.mover(-1,0)
                    L.auto_pinta()

                if event.key == pygame.K_RIGHT:
                    L.auto_borra()
                    L.mover(1,0)
                    L.auto_pinta()

            pygame.display.update()

Detección de colisiones por la propia pieza

Estamos orientando nuestra programación a objetos: si bien el bucle principal todavía es un guión en Python (cosa que mejoraremos antes de terminar de escribir el juego), nuestra pieza es un objeto que tiene datos y comportamiento. Hasta ahora el comportamiento era girar, moverse y representarse en la pantalla.

Normalmente, un objeto sufre un efecto si colisiona con otro y, de alguna forma, modifica su comportamiento. En este caso, para incorporar al juego los límites de la pantalla nos basta con poder preguntarle a la pieza si está colisionando con otra pieza, o con los bordes de la pantalla.

Vamos a crear una función en nuestra pieza que devuelva True  si está en colisión con “el resto”, y False  en caso contrario. “El resto” será el conjunto formado por los bordes de la pantalla y el resto de piezas que se hayan ido depositando. Como veremos más adelante, lo modelaremos como un todo que se irá ampliando cada vez que vayan cayendo piezas. “El resto”, por lo tanto, tendrá una matriz de dimensiones iguales a las de la pantalla y “1” en los bordes más en las piezas que se han ido depositando.

En este caso, en lugar de detectar colisiones gráficas detectaremos colisiones usando la matriz que representa a la pieza, es decir, el campo .matriz  del objeto Pieza .

Sobre el código resultante del programa anterior, que está más arriba en este artículo, añade a la clase Pieza  la siguiente función:

    def colisiona_con(self, resto):
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] == 1:
                    if resto.matriz[self.y + i][self.x + j] == 1:
                        return True
        return False

El código de arriba devuelve un resultado positivo si ambas matrices tienen un 1 en alguna coordenada de la pantalla en la que coincidan. Leyéndolo en voz alta, lo que hace este método de la pieza es lo siguiente:

  • Para cada fila de la matriz de la pieza,
  • Recorro todas sus celdas (columnas),
  • Si la matriz de la pieza, en la coordenada formada por la fila y la columna actual, tiene un 1,
  • Compruebo si en esa coordenada de la pantalla, relativa a la posición actual de mi pieza, hay un “1” en la matriz de “el resto”.

En resumen, la forma de modelar “el resto” y esta función tiene truco, y está adaptada a la mecánica del Tetris: funciona y se entiende correctamente si pensamos que la actual (self) es la que se mueve, y que cualquier otra pieza está ya colocada y estática en una determinada posición de la pantalla.

A partir de ahora, llamaremos a “el resto” Recipiente, porque recuerda a un recipiente (o un vaso) que se va llenando de piezas.

Modelar los bordes y las piezas depositadas: objeto “Recipiente”

No sé si el nombre es lo más correcto, pero alguno hay que ponerle y “el resto” no es muy descriptivo. “Recipiente” puede valer, por el momento.

El Recipiente comparte bastante lógica con la Pieza; al menos toda la lógica que le permite pintarse en la pantalla. Aunque no sea lo más eficiente a ojos del programador experimentado y por ello más adelante reorganizaremos la jerarquía de objetos del juego para ahorrar recursos, de momento haremos lo siguiente:

  • Crearemos una clase que heredará de pieza, para reutilizar la infraestructura gráfica de la misma:
    • Su matriz
    • Las funciones de dibujarse cuadrado a cuadrado.
  • Le añadiremos un método que permitirá añadir a su matriz los “1” de la pieza que se acabe por depositar a medida que vaya cayendo.

La definición de la clase Recipiente  es la siguiente:

class Recipiente(Pieza):
    def incorporar(self, otra_pieza):
        'incorpora la matriz de otra pieza a la de si mismo'
        for i in range(0,len(otra_pieza.matriz)):
            for j in range(0,len(otra_pieza.matriz[i])):
                if otra_pieza.matriz[i][j] == 1:
                    self.matriz[otra_pieza.y+i][otra_pieza.x+j] = otra_pieza.matriz[i][j]

Lo que hace es añadir los “1” de la pieza que se pasa como parámetro en las coordenadas del recipiente donde se encuentre en ese momento. Esta funcionalidad será la que usaremos cuando una pieza colisione con algo “desde arriba”, es decir, cuando caiga encima del recipiente o de su contenido.

Sin embargo, hay que tener en cuenta una cosa: para poder cambiar el contenido de los elementos de una matriz, ésta no puede estar formada por tuplas (por ejemplo, (0, 1, 0) ), sino que tiene que estar formada por listas (por ejemplo, [0, 1, 0]). Así pues, en la siguiente sección, en la que veremos las modificaciones que hay que hacer al programa principal, tendremos que ser cuidadosos al definir la matriz del Recipiente.

Bucle principal

El bucle principal tiene que ser ampliado para implementar las siguientes cosas:

  1. La definición del objeto recipiente , es decir, la definición de una instancia de la clase Recipiente .
    • Esto va a implicar agrandar la pantalla: si queremos representar el borde de la pantalla como cuadrados visibles sin restar área de juego.
    • También habrá que definir un color nuevo para representar esos cuadrados.
  2. Un mecanismo que permita acelerar la gravedad si la tecla que se pulsa es la flecha hacia abajo
  3. Los rebotes: si una pieza está colisionando con el recipiente, hay solape entre dos cuadrados: es necesario deshacer el último paso del movimiento antes de redibujarla.
    • Esto también hay que controlarlo al hacer giros de las piezas: concretamente y por cómo está programado el giro, puede haber una colisión a la derecha de la pieza que gira.
  4. El depósito de la pieza cuando la colisión es por gravedad, o desde arriba:
    • La pieza que cae debe pasar a formar parte del recipiente
    • Debemos tener una forma de “pedir otra pieza” para repetir el ejercicio una y otra vez. Por este artículo pediremos al juego otra pieza igual.

El código del bucle principal es el siguiente, sobre el que es muy fácil localizar los puntos arriba mencionados (además, os resalto las líneas de código que merecen vuestra atención):

def siguiente_pieza():
    L = Pieza (matriz_L, MAGENTA, NEGRO, pantalla, SLOT)
    L.pinta(4, 0)
    return L


BLANCO = (255,255,255)
GRIS = (127,127,127)
NEGRO = (0, 0, 0)
MAGENTA = (200,0,200)
FPS = 60
SLOT = 40

# pantalla 12 x 22 y posición inicial de la pieza
ANCHO = 12*SLOT
ALTO = 22*SLOT
x_ini = 4
y_ini = 0

# inicializamos pygame
pygame.init()

# definición de la pantalla
pantalla = pygame.display.set_mode((ANCHO, ALTO))
pantalla.fill(NEGRO)

matriz_L = [(1, 0),
            (1, 0),
            (1, 1)]

matriz_Recipiente = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]

recipiente = Recipiente (matriz_Recipiente, GRIS, NEGRO, pantalla, SLOT)
recipiente.pinta(0, 0)
L = siguiente_pieza()

pygame.display.update()

# reloj de control de refresco
clock = pygame.time.Clock()

# Evento de gravedad
GRAVEDAD = pygame.USEREVENT + 1
t_GRAVEDAD = 2000 #milisegundos
pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)

continuar = True


while continuar: 
    # pausa hasta el siguiente "tick" de reloj
    clock.tick(FPS)

    # detección de evento QUIT (aspa)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            continuar = False
        else:
            # Procesar la gravedad
            if event.type == GRAVEDAD:
                L.auto_borra()
                L.mover(0, 1)
                if L.colisiona_con(recipiente):
                    t_GRAVEDAD = 2000
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
                    L.mover(0,-1)
                    L.auto_pinta()
                    # Incorporar pieza a "P"
                    recipiente.incorporar(L)
                    L = siguiente_pieza()
                else:
                    L.auto_pinta()
     
            
        
            if event.type == pygame.KEYDOWN:

                # El giro de las piezas puede provocar colisiones por el lado derecho
                if event.key == pygame.K_z:
                    L.auto_borra()
                    L.rotarM90()
                    if L.colisiona_con(recipiente):
                        L.mover(-1,0)
                    L.auto_pinta()

                if event.key == pygame.K_x:
                    L.auto_borra()
                    L.rotar90()
                    if L.colisiona_con(recipiente):
                        L.mover(-1,0)
                    L.auto_pinta()

                if event.key == pygame.K_LEFT:
                    L.auto_borra()
                    L.mover(-1,0)
                    if L.colisiona_con(recipiente):
                        L.mover(1, 0)
                    L.auto_pinta()

                if event.key == pygame.K_RIGHT:
                    L.auto_borra()
                    L.mover(1,0)
                    if L.colisiona_con(recipiente):
                        L.mover(-1, 0)
                    L.auto_pinta()

                # acelerar gravedad
                if event.key == pygame.K_DOWN:
                    t_GRAVEDAD = 70
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
                else:
                    t_GRAVEDAD = 2000
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)

            
            pygame.display.update()

Fijaos que no controlamos el hecho de que las piezas “rebosen de la pantalla por arriba”, que sería la condición final del juego. Eso lo dejaremos para otro artículo.

Por último, sólo faltan ya unas pequeñas correcciones a la clase que representa a las piezas para que, cuando se re-pinte (más bien, cuando se borre), no borre por accidente partes del recipiente que puedan quedar en los entresijos de nuestras piezas.

Ajuste fino a la parte gráfica del objeto que modela la pieza

La clase pieza original borra “de un plumazo” todo el rectángulo de la pantalla que está ocupado por la pieza que se va moviendo. Esto puede dar problemas cuando estamos encajando la pieza, mientras cae, en “la montaña” que representan las piezas depositadas en el recipiente (pruébalo si quieres).

Para arreglar ese problema, lo que tienes que hacer es reprogramar la función de borrado para que sólo borre aquellos cuadrados visibles, es decir, los que se corresponden con posiciones a “1” de la matriz de la pieza.

Una solución puede ser ésta:

    def borra(self, x_ini, y_ini):
        'borra la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] == 1:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, self.fondo)

En ella, pintamos del color del fondo a todos los cuadrados que se corresponden con posiciones de la matriz a “1”.

Resultado final

Poniendo todas estas modificaciones juntas y probándolas, obtendremos un comportamiento como el representado por este Vine:

En la siguiente entrega viene programaremos la mecánica de las líneas y la sucesión aleatoria de las distintas piezas del juego. Si hay algo que refinar de todo lo hecho hasta ahora, lo iremos haciendo. Hasta entonces no dudes en ponerte en contacto conmigo si tuvieras cualquier duda, tanto usando los comentarios de este artículo como a través del formulario de contacto.

El código completo del programa de hoy se encuentra en el siguiente cuadro, que tendrás que expandir para poderlo copiar por si te hace falta.

import pygame

class Pieza:
    'Modela una pieza de Cuatris :)'
    def __init__(self, matriz, color, fondo, pantalla, lado_cuadrado):
        self.matriz = matriz
        self.color = color
        self.fondo = fondo
        self.pantalla = pantalla
        self.lado_cuadrado = lado_cuadrado
        # posición de la pieza
        self.x = 0
        self.y = 0
        self.calcula_dimensiones()

    def calcula_dimensiones(self):
        self.filas = len(self.matriz)
        self.columnas = len(self.matriz[0])
        
    def rotar90(self):
        'Rota la pieza en el sentido horario'
        self.matriz = list(zip(*self.matriz[::-1]))
        self.calcula_dimensiones()
        
    def rotarM90(self):
        'Rota la pieza en el sentido antihorario'
        self.matriz = list(zip(*self.matriz))[::-1]
        self.calcula_dimensiones()

    def __pinta_cuadrado(self, x, y, color):
        'dibuja un cuadrado en las coordenadas de malla especificadas'
        cuadrado = (x*self.lado_cuadrado,
                    y*self.lado_cuadrado,
                    self.lado_cuadrado,
                    self.lado_cuadrado)
        cuadrado_interno = (x*self.lado_cuadrado+8,
                            y*self.lado_cuadrado+8,
                            self.lado_cuadrado-16,
                            self.lado_cuadrado-16)
        pygame.draw.rect(self.pantalla,
                         (int(color[0]/2),
                          int(color[1]/2),
                          int(color[2]/2)),
                         cuadrado,
                         0)
        pygame.draw.rect(self.pantalla, color, cuadrado_interno, 0)

    def __borra_rectangulo(self, rectangulo):
        'borra el área especificada'
        pygame.draw.rect(self.pantalla, self.fondo, rectangulo, 0)

    def pinta(self, x_ini, y_ini):
        'pinta la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        # actualizamos la posición de la pieza
        self.x = x_ini
        self.y = y_ini
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] == 1:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, self.color)
                    
    def borra(self, x_ini, y_ini):
        'borra la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] == 1:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, self.fondo)

    # Movimiento de la pieza
    def mover(self, desplazamiento_x, desplazamiento_y):
        'mueve la pieza el desplazamiento indicado (sin pintarla), en coordenadas de malla'
        self.x = self.x + desplazamiento_x
        self.y = self.y + desplazamiento_y

    # Funciones que hacen uso del estado de la pieza
    def auto_pinta(self):
        'pintar la pieza en las propiedades autocontenidas'
        self.pinta(self.x, self.y)

    def auto_borra(self):
        'borrar la piezaden las propiedades autocontenidas'
        self.borra(self.x, self.y)

    def colisiona_con(self, resto):
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] == 1:
                    if resto.matriz[self.y + i][self.x + j] == 1:
                        return True
        return False

class Recipiente(Pieza):
    def incorporar(self, otra_pieza):
        'incorpora la matriz de otra pieza a la de si mismo'
        for i in range(0,len(otra_pieza.matriz)):
            for j in range(0,len(otra_pieza.matriz[i])):
                if otra_pieza.matriz[i][j] == 1:
                    self.matriz[otra_pieza.y+i][otra_pieza.x+j] = otra_pieza.matriz[i][j]
                    

def siguiente_pieza():
    L = Pieza (matriz_L, MAGENTA, NEGRO, pantalla, SLOT)
    L.pinta(4, 0)
    return L


BLANCO = (255,255,255)
GRIS = (127,127,127)
NEGRO = (0, 0, 0)
MAGENTA = (200,0,200)
FPS = 60
SLOT = 40

# pantalla 12 x 22 y posición inicial de la pieza
ANCHO = 12*SLOT
ALTO = 22*SLOT
x_ini = 4
y_ini = 0

# inicializamos pygame
pygame.init()

# definición de la pantalla
pantalla = pygame.display.set_mode((ANCHO, ALTO))
pantalla.fill(NEGRO)

matriz_L = [(1, 0),
            (1, 0),
            (1, 1)]

matriz_Recipiente = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]

recipiente = Recipiente (matriz_Recipiente, GRIS, NEGRO, pantalla, SLOT)
recipiente.pinta(0, 0)
L = siguiente_pieza()

pygame.display.update()

# reloj de control de refresco
clock = pygame.time.Clock()

# Evento de gravedad
GRAVEDAD = pygame.USEREVENT + 1
t_GRAVEDAD = 2000 #milisegundos
pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)

continuar = True


while continuar: 
    # pausa hasta el siguiente "tick" de reloj
    clock.tick(FPS)

    # detección de evento QUIT (aspa)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            continuar = False
        else:
            # Procesar la gravedad
            if event.type == GRAVEDAD:
                L.auto_borra()
                L.mover(0, 1)
                if L.colisiona_con(recipiente):
                    t_GRAVEDAD = 2000
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
                    L.mover(0,-1)
                    L.auto_pinta()
                    # Incorporar pieza a "P"
                    recipiente.incorporar(L)
                    L = siguiente_pieza()
                else:
                    L.auto_pinta()
     
            
        
            if event.type == pygame.KEYDOWN:

                # El giro de las piezas puede provocar colisiones por el lado derecho
                if event.key == pygame.K_z:
                    L.auto_borra()
                    L.rotarM90()
                    if L.colisiona_con(recipiente):
                        L.mover(-1,0)
                    L.auto_pinta()

                if event.key == pygame.K_x:
                    L.auto_borra()
                    L.rotar90()
                    if L.colisiona_con(recipiente):
                        L.mover(-1,0)
                    L.auto_pinta()

                if event.key == pygame.K_LEFT:
                    L.auto_borra()
                    L.mover(-1,0)
                    if L.colisiona_con(recipiente):
                        # deshacer
                        L.mover(1, 0)
                    L.auto_pinta()

                if event.key == pygame.K_RIGHT:
                    L.auto_borra()
                    L.mover(1,0)
                    if L.colisiona_con(recipiente):
                        # deshacer
                        L.mover(-1, 0)
                    L.auto_pinta()

                # acelerar gravedad
                if event.key == pygame.K_DOWN:
                    t_GRAVEDAD = 70
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
                else:
                    t_GRAVEDAD = 2000
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)

            
            pygame.display.update()

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *