Archivo de la etiqueta: Videojuegos

Cuatris v0.9: nuestro clon de Tetris está casi listo

Poco nos queda ya para tener un clon de Tetris con el que poder jugar con nuestros amigos y familiares. Lo que vamos a hacer en este artículo, quizá cambiando un poco la estructura de la serie, es mostrar un recuadro con la siguiente pieza que saldrá, la puntuación del jugador (en nº de líneas), el nivel en el que está y cuántas líneas le quedan para pasar al siguiente nivel.

El Cuatris: casi, casi terminado
El Cuatris: casi, casi terminado

Recapitulando, hasta ahora hemos hecho los siguientes avances:

  1. Dibujar una pieza (básica) y programar la rotación: lo vimos en este artículo
  2. Programar la gravedad y el resto de movimientos: lo vimos en este otro artículo
  3. Colisiones de la pieza con otras piezas y el borde de la pantalla: fue el en esta otra entrada
  4. Haciendo líneas y retocando la mecánica anterior: el último artículo de la serie
  5. Puntuaciones y niveles: el artículo de hoy
  6. Borde, marcador, tabla de puntuaciones.
  7. Refinando los gráficos de las piezas

Para el siguiente capítulo lo que haremos será aplicar una actividad llamada refactorización de forma que tengamos el videojuego 100 % orientado a objetos y sea muy fácil implementar una opción para repetir la partida si el jugador pierde, dándolo así por terminado y dejándoos a vosotros el resto de mejoras que se os ocurran.

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.

Sigue leyendo Cuatris v0.9: nuestro clon de Tetris está casi listo

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!

Sigue leyendo Paredes y colisiones en nuestro clon de Tetris

Aplicando gravedad a nuestro clon de Tetris

En esta semana vamos a retomar Cuatris, nuestro clon de Tetris, donde lo dejamos la pasada semana. Recordando el plan, la secuencia que íbamos a seguir es la siguiente:

  1. Dibujar una pieza (básica) y programar la rotación: lo vimos la semana pasada
  2. Programar la gravedad y el resto de movimientos: el artículo de hoy
  3. Colisiones de la pieza con otras piezas y el borde de la pantalla.
  4. Haciendo líneas y retocando la mecánica anterior
  5. Puntuaciones y niveles
  6. Borde, marcador, tabla de puntuaciones
  7. Refinando los gráficos de las piezas

Lo que haremos para eso va a ser usar los eventos de usuario que proporciona Pygame, de tal modo que nos adecuaremos a las reglas internas de funcionamiento del bucle de procesado de eventos que ya tenemos, programando la ocurrencia de un evento cada cierto tiempo (dos segundos por ahora).

Además, programaremos dos movimientos más, izquierda y derecha, a lo largo de una pantalla que ya redimensionaremos a su tamaño definitivo (10 cuadros de ancho por 20 de alto).

¡Manos a la obra!

(Foto: Museos Científicos Coruñeses)

Sigue leyendo Aplicando gravedad a nuestro clon de Tetris

Episodio 23 – Videojuegos y aprendizaje

En el episodio de hoy os hablo de videojuegos desde el punto de vista del aprendizaje: qué aportan tanto al que juega como al que usa la programación de videojuegos como vehículo para aprender a programar.

Enlaces del episodio:

Espero vuestros comentarios en http://pitando.net

Eventos en Pygame (1) – Mover un objeto con tu teclado

Si has seguido los dos artículos que han aparecido acerca de Pygame, habrás notado que el programa se articula en torno a un bucle que controla unas variables llamadas genéricamente “eventos”, esto es, cosas que han pasado. Como muchas librerías que proporcionan mecanismos para desarrollar interactividad con el usuario, Pygame proporciona un bucle de eventos, o eventos en un bucle. Esto significa que cada cierto tiempo nos da la oportunidad de saber qué ocurre en los dispositivos de entrada (teclado, ratón, joystick) y nos proporciona esa información en orden, en una estructura de tipo lista ordenada. En general, los eventos se deberían procesar en modo cola: las colas son estructuras de datos en cuya cabecera encontraremos el primer elemento que se ha introducido, y podremos así consultar su contenido en el orden correcto. En Pygame los obtendremos en forma de lista, pudiendo procesarlos en orden, pero también acceder aleatoriamente a los elementos de la lista.

Vamos a ver los tipos de eventos que ofrece Pygame para el teclado y cómo se comportan, reutilizando para ello el programa del artículo anterior.

El bucle de procesado de eventos de Pygame

El bucle de eventos es la pieza central del programa, y en ella podemos comprobar las cosas que han ido ocurriendo y traducirlas en efectos sobre la pantalla. Lo hemos hecho con un fragmento de código que suele tener una estructura como la siguiente

while [condición]:
    for event in pygame.event.get():
        if event.type == [evento]:
            [acción]

Es decir, mientras se cumpla una determinada condición, extraeremos los eventos de la cola y los examinaremos uno a uno, programando acciones en nuestro programa con respecto al tipo (y contenido) del evento. Para eso, obtendremos el contenido de la cola en forma de lista ordenada mediante pygame.event.get()

Visto así, Pygame es una librería orientada a eventos, puesto que la forma de controlar el ritmo del juego que nos propone es precisamente procesar los eventos que se registran entre una pasada y otra del bucle. Pero, la cuestión: ¿cómo de frecuente debe ser el bucle para que el programa funcione de forma fluida, pero sin hacer que la frecuencia del mismo degrade el rendimiento?

En general, desde el punto de vista del programador, no interesa consultar los eventos a más frecuencia que la de refresco en pantalla puesto que la mayoría de periféricos de entrada y de reacciones del usuario son más lentas que la pantalla. En los vídeos de alta resolución, esta frecuencia es de 60 Hz, es decir, 60 cuadros por segundo.

Para realizar este control, Pygame nos ofrece un reloj que permite estructurar el programa en intervalos temporales, de tal forma que podamos esperar a que se cumpla una determinada frecuencia. Lo hemos usado ya, son las líneas resaltadas de este código esquemático:

FPS = [valor] # frecuencia: nº de veces por segundo.
clock = pygame.time.Clock()

while [condición]
    clock.tick(FPS) # pausa hasta el siguiente "tick" de reloj

    for event in pygame.event.get():
        if event.type == [evento]:
            [acción]

clock.tick(FPS)  suspende la ejecución del programa hasta que el tiempo no llega a la siguiente fracción de segundo de la duración que hemos definido (1/FPS segundos).

Procesando teclas: eventos de teclado

Vamos a ver muy brevemente cómo mover el objeto que ya teníamos construido en el artículo anterior. Para ello os presento primero el programa modificado en donde podréis ver dos novedades (resaltadas en el código):

  • He movido la acción de mover el cuadrado por la pantalla a la propia clase Cuadrado , creando un nuevo método en ella, mover_p  a la que le podemos proporcionar la pantalla como parámetro, de forma que se pueda encapsular el borrado y el redibujado de las zonas afectadas por el movimiento.
  • Por otro lado, hay código nuevo en el bucle de eventos, que analizaré a continuación. Puedes ejecutar primero el programa y ver que, ahora, el objeto se puede mover en horizontal con las flechas del cursor. Sin embargo, se mueve “por pasos”, es decir: sólo detecta una pulsación y lo mueve de 20 en 20 píxels.
import pygame

BLANCO = (255,255,255)
NEGRO = (0, 0, 0)
MAGENTA = (255,0,255)
FPS = 60

class Cuadrado:
    def __init__(self, x, y, lado, color, fondo):
        self.x = x
        self.y = y
        self.color = color
        self.fondo = fondo
        self.lado = lado
        self.rect = pygame.Rect(self.x,
                                self.y,
                                self.lado,
                                self.lado)

    def __pinta(self, pantalla, color):
        'Realiza el dibujo efectivo'
        pygame.draw.rect(pantalla, color, self.rect, 0)
        
    def pinta(self, pantalla):
        'Pinta el cuadrado con el color propio'
        self.__pinta(pantalla, self.color)

    def borra(self, pantalla):
        'Borra el cuadrado'
        # Realmente lo pinta con el color de fondo
        self.__pinta(pantalla, self.fondo)

    def colisiona_con(self, cuadrado2):
        'Comprueba la colisión con otro cuadrado'
        return self.rect.colliderect(cuadrado2.rect)

    def mover(self, avance_x, avance_y):
        'avance del cuadrado en un cuadro (frame)'
        self.rect.move_ip(avance_x, avance_y)

    def mover_p(self, pantalla, avance_x, avance_y):
        'avance del cuadrado en un cuadro, con refresco de pantalla'
        cuadrado.borra(pantalla)
        rect_inicial = cuadrado.rect.copy()
        cuadrado.mover(avance_x, avance_y)
        rect_final = cuadrado.rect.copy()
        cuadrado.pinta(pantalla)
        pygame.display.update(rect_final.union(rect_inicial))


# inicializamos pygame
pygame.init()

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

cuadrado = Cuadrado(50,50, 35, BLANCO, NEGRO)
cuadrado2 = Cuadrado(540,50,35, MAGENTA, NEGRO)
cuadrado.pinta(pantalla)
cuadrado2.pinta(pantalla)
pygame.display.update()

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

while not cuadrado.colisiona_con(cuadrado2):
    # 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()
    
        # Movimiento
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                cuadrado.mover_p(pantalla, -20, 0)

            if event.key == pygame.K_RIGHT:
                cuadrado.mover_p(pantalla, 20, 0)

    
print("Hay solape")
    
pygame.time.delay(3000)
pygame.quit()

Vamos a examinar el bucle que procesa los eventos:

  • Ya sabemos que para obtenerlos podemos invocar a pygame.event.get(), el cual los devuelve en una lista sobra la que podemos iterar con cualquier construcción de tipo bucle.
  • Dentro del bucle, comprobaremos para cada evento su tipo mediante el valor event.type . En nuestro programa controlamos los eventos de tipo pygame.QUIT ,  y pygame.KEYDOWN  (“tecla abajo”).
    • Una vez detectado el tipo de evento que nos interesa, y atendiendo a esto, podemos examinar la información que trae dicho evento como contenedor de datos.
    • En este caso, nos interesa el atributo event.key, que nos dice la tecla pulsada.
    • Para este artículo reaccionaremos ante pygame.K_LEFT  y pygame.K_RIGHT , que se corresponden con las flechas izquierda y derecha del cursor.

El funcionamiento de este programa es “paso a paso”, es decir, Pygame sólo va a proporcionar una pulsación de teclas sin importar el tiempo que mantengamos la tecla pulsada. Así pues, no responde a un movimiento fluido.

Para conseguir un movimiento fluido basta con configurar el módulo de teclado con la sentencia que os he resaltado en la siguiente versión del programa (también he modificado las líneas que ejecutan el movimiento para suavizarlo un poco):

import pygame

BLANCO = (255,255,255)
NEGRO = (0, 0, 0)
MAGENTA = (255,0,255)
FPS = 60

class Cuadrado:
    def __init__(self, x, y, lado, color, fondo):
        self.x = x
        self.y = y
        self.color = color
        self.fondo = fondo
        self.lado = lado
        self.rect = pygame.Rect(self.x,
                                self.y,
                                self.lado,
                                self.lado)

    def __pinta(self, pantalla, color):
        'Realiza el dibujo efectivo'
        pygame.draw.rect(pantalla, color, self.rect, 0)
        
    def pinta(self, pantalla):
        'Pinta el cuadrado con el color propio'
        self.__pinta(pantalla, self.color)

    def borra(self, pantalla):
        'Borra el cuadrado'
        # Realmente lo pinta con el color de fondo
        self.__pinta(pantalla, self.fondo)

    def colisiona_con(self, cuadrado2):
        'Comprueba la colisión con otro cuadrado'
        return self.rect.colliderect(cuadrado2.rect)

    def mover(self, avance_x, avance_y):
        'avance del cuadrado en un cuadro (frame)'
        self.rect.move_ip(avance_x, avance_y)

    def mover_p(self, pantalla, avance_x, avance_y):
        'avance del cuadrado en un cuadro, con refresco de pantalla'
        cuadrado.borra(pantalla)
        rect_inicial = cuadrado.rect.copy()
        cuadrado.mover(avance_x, avance_y)
        rect_final = cuadrado.rect.copy()
        cuadrado.pinta(pantalla)
        pygame.display.update(rect_final.union(rect_inicial))


# inicializamos pygame
pygame.init()

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

cuadrado = Cuadrado(50,50, 35, BLANCO, NEGRO)
cuadrado2 = Cuadrado(540,50,35, MAGENTA, NEGRO)
cuadrado.pinta(pantalla)
cuadrado2.pinta(pantalla)
pygame.display.update()

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

while not cuadrado.colisiona_con(cuadrado2):
    # 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()
    
        # Movimiento
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                cuadrado.mover_p(pantalla, -2, 0)

            if event.key == pygame.K_RIGHT:
                cuadrado.mover_p(pantalla, 2, 0)

    
print("Hay solape")
    
pygame.time.delay(3000)
pygame.quit()
    

En el sentido de ampliar esta sencilla técnica con todas sus posibilidades, y ya para terminar este artículo, os dejo en el apartado de referencias una serie de enlaces que tener a mano para dominar los eventos de teclado (están en inglés). Con lo que sabéis ya podéis hacer un pequeño experimento de juego en el que un cuadrado tenga que llegar hasta el otro esquivando objetos que se muevan por la pantalla. ¿Os atrevéis?

No dudéis en dejarme vuestros comentarios en este artículo o a través del formulario de contacto.

Referencias

  • Referencia de los eventos en Pygame: qué tipos de eventos hay tanto de teclado como de otros dispositivos, cuál es su estructura, y cómo gestionarlos o leer su contenido: http://www.pygame.org/docs/ref/event.html
  • Mapa de teclado – teclas que reconoce Pygame: http://www.pygame.org/docs/ref/key.html. En este enlace, además, encontraréis la referencia completa del teclado para poderlo configurar.