Llegar más lejos con el módulo Sprite, ejemplos aplicados
Introducción
El propósito de este capítulo es profundizar en la utilización de los sprites en Pygame, experimentando entre otras cosas con las facilidades ofrecidas para la gestión de colisiones. Por lo tanto, crearemos varios videojuegos utilizando todas las nociones estudiadas anteriormente, comenzando, por supuesto, por los sprites.
Este capítulo se centra en varios ejemplos de videojuegos. En primer lugar, el mítico juego de la serpiente (snake). El principio de este juego es que su cabeza nunca debe tocar su cuerpo (en cuyo caso, pierde). Pero cada vez que come, gana un punto y crece, lo que hace que sea difícil (y divertido) moverse cuando ha pasado un tiempo. La comida aparece al azar y desaparece. La serpiente se mueve usando las flechas de dirección.
Continuaremos con un pequeño juego de laberintos en el que se disponen objetos. El objetivo del personaje es recoger todos los objetos, moviéndose dentro del laberinto. Cronometramos el juego. De nuevo, movemos el personaje usando las flechas del teclado.
Posteriormente, construiremos un juego de romper ladrillos. Al igual que el juego de la serpiente, es un clásico de los videojuegos. El jugador mueve una raqueta lateralmente que le permite hacer rebotar una pelota. La pelota puede romper un conjunto de ladrillos colocados en la parte superior de la pantalla. El objetivo obviamente es romper todos los ladrillos.
Por último, vamos...
El juego de la serpiente (snake)
1. El contexto
Como se acaba de mencionar, este juego es realmente mítico. Se inventó en 1976, se ha implementado en muchos sistemas y basa su originalidad en el hecho de que el obstáculo al movimiento de la serpiente es, después de un cierto tiempo, la propia serpiente. De hecho, la serpiente crece cada vez que devora alimentos. Cuando la serpiente come comida, se gana un punto. Para terminar, la comida aparece en lugares elegidos al azar que permanecen visibles durante unos segundos. Posteriormente, otros elementos alimenticios aparecen en otros lugares y desaparecen, etc.
2. Las imágenes utilizadas
Vamos a empezar definiendo las imágenes que se utilizarán en este juego.
-
Una imagen para representar la cabeza de la serpiente. No importa cuál es su orientación. Vamos a gestionar esto con código, utilizando rotaciones.
-
Una imagen que representa un elemento del cuerpo de la serpiente.
-
Una imagen que representa la comida violeta ingerida por la serpiente.
A continuación, se muestran las tres imágenes en formato PNG. Es esencial tener las mismas dimensiones para cada una de las imágenes, con el fin de asegurarse de que el enfoque basado en sprites no se distorsiona. Cada una de las imágenes es de forma cuadrada y tiene una dimensión de 32 x 32 píxeles.
-
Cabeza de la serpiente:
-
Cuerpo de la serpiente:
-
Un elemento alimenticio:
3. Efectos de sonido utilizados
Prevemos dos efectos de sonido: uno que se reproduce cuando aparece la comida y cuando la serpiente se la come, y otro cuando se pierde el juego. Ambos archivos tienen una extensión .wav.
4. El programa global
Aquí está el código general del juego, así como una captura de pantalla del juego. A continuación, vamos a explicar todos los aspectos del código. Procederemos de la misma manera para cada uno de los siguientes juegos.
Tenemos tres clases que heredan de la clase Sprite de Pygame.
-
La clase SERPIENTE que se define como una cabeza a la que podemos añadir elementos de cuerpo (de la serpiente), aquí instancias de la clase CUERPO.
-
La clase CUERPO que representa un elemento del cuerpo de la serpiente.
-
La clase COMIDA que se utiliza para organizar diferentes alimentos en la ventana del juego.
import pygame, random sys
pygame.mixer.init()
NEGRO = (0, 0, 0)
BLANCO...
El juego del laberinto
1. El contexto
Definimos un pequeño juego del laberinto cuyo objetivo es recoger diez objetos dispuestos aleatoriamente en los pasillos del laberinto, en el mínimo tiempo posible. Por lo tanto, mostraremos un cronómetro para que el jugador pueda ver el tiempo consumido.
Presentamos la gestión de colisiones que habrá que hacer:
-
Queremos comprobar que los objetos están en los pasillos y no en las paredes del laberinto. El personaje debe tener acceso a los objetos.
-
Cuando el personaje recoge un objeto, se trata de una colisión que se debe manejar de manera similar a lo que hemos hecho cuando la serpiente (juego anterior) come comida.
Podríamos haber colocado al personaje en una entrada y con la misión adicional de salir por una salida determinada después de recoger todos los objetos, pero la mayor parte del desarrollo y la manipulación de los sprites no se centra en esa parte. Esta elección también permite simplificar el ejemplo y lo hace más legible.
2. Diseño del laberinto
Vamos a reflexionar sobre cómo mostrar el laberinto. Es necesario definir su recorrido y comprobar que cada punto del laberinto está conectado con cualquier otro punto del laberinto, para estar seguros de que el personaje será capaz de recuperar todos los objetos. Si pensamos en el laberinto como en una red, queremos definir una red conectada.
El enfoque más sensato es definir el laberinto en un archivo externo y definir un código para indicar que en un punto determinado hay una pared y en otro lugar un pasillo.
A continuación, se muestra el contenido del archivo Laberinto.txt donde el carácter M representa una pared y el carácter espacio representa un pasillo.
MMMMMMMMMMMMMMMMMMMMMMMMM
M M
MMMMMMM MMMMM MMMM MMMM M
M M
MMMMMMMMMM MMMMMMMMMMMMMM
M MMM MM M
MMMM M MM MMM M MM
M M MMMMMMMMMMM MMM M M ...
El juego de romper ladrillos
1. El contexto
El juego de arcade de romper ladrillos (breakout en inglés), se remonta a los inicios de los videojuegos. Se implementó en 1975, y desde entonces, existen docenas de juegos de este tipo, que se ejecutan en las plataformas más variadas. También es un juego al que se pueden añadir varios niveles (levels).
El objetivo es ganar puntos rompiendo ladrillos con una pelota que rebota en una raqueta, colocada en la parte inferior de la ventana del juego. Tenga cuidado, si la pelota llega al fondo de la ventana del juego, es decir, la raqueta no la ha devuelto, entonces pierde el juego.
2. Las imágenes utilizadas
Una vez más, las imágenes se corresponden con el tamaño exacto de los futuros sprites.
Tenemos tres imágenes de formas geométricas muy sencillas, que se pueden crear con software de dibujo como Gimp.
-
Una pelota verde:
-
Un elemento "ladrillo" de color azul:
-
Una raqueta naranja:
3. El programa global
En este caso tenemos dos archivos:
-
CLASES.py que incluye las clases del juego y las constantes.
-
ROMPER-LADRILLOS.py con el algoritmo y el bucle del juego.
En este programa se utiliza el operador +=. Añade un valor numérico al valor de la variable que está a su izquierda y le asigna la suma obtenida. Ejemplo:
>>> x = 4
>>> x += 3
>>> print(x)
7
CLASES.py
import pygame
ANCHURA, ALTURA = 640, 480
PELOTA_ANCHURA, PELOTA_ALTURA = 16, 16
LADRILLO_ANCHURA, LADRILLO_ALTURA = 64, 16
RAQUETA_ANCHURA, RAQUETA_ALTURA = 64, 16
RAQUETA_VELOCIDAD = 20
PELOTA_VELOCIDAD = 2
class OBJETO(pygame.sprite.Sprite):
def __init__(self, IMAGE):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(IMAGE).convert()
self.image.set_colorkey((255, 255, 255))
self.rect = self.image.get_rect()
class RAQUETA(OBJETO):
def __init__(self, IMAGE):
OBJETO.__init__(self, IMAGE)
self.rect.bottom = ALTURA
self.rect.left = (ANCHURA - self.image.get_width()) / 2
def MoverIzquierda(self):
if self.rect.left > 0:
self.rect.move_ip(-RAQUETA_VELOCIDAD...
El juego de desplazamiento: Cohete y planetas (versión 2)
1. El contexto
Retomamos el juego del capítulo Conceptos del videojuego y primeros pasos con Pygame, programado sin usar el módulo sprites, que induce cálculos geométricos bastante tediosos para controlar las posibles colisiones entre cohetes y planetas. De hecho, el objetivo del juego es evitar el choque entre el cohete que se mueve lateralmente y los planetas, que se desplazan hacia arriba y hacia abajo.
2. El programa global
Tenemos tres archivos Python en el juego.
-
El archivo Main.py original.
-
El archivo CLASES.py que incluye las clases COHETE y PLANETA.
-
El archivo CONSTANTES.py que agrupa las diferentes constantes del juego.
CONSTANTES.py
from random import *
ALTURA_VENTANA = 600
ANCHURA_VENTANA = 600
COLOR_FONDO = (255, 255, 250)
PARAR_JUEGO = False
ANCHURA_COHETE = 88
ALTURA_COHETE = 175
MOVIMIENTO_XX_COHETE = 0
XX_PLANETA = randint(30, 130)
YY_PLANETA = 20
ANCHURA_PLANETA = 111
ALTURA_PLANETA = 80
XX_ENTRE_PLANETAS = 350
YY_ENTRE_PLANETA = 125
VELOCIDAD_PLANETAS = 3
CLASES.py
import pygame, random, sys
from CONSTANTES import *
from datetime import timedelta, datetime, date, time
LISTA_PLANETAS = pygame.sprite.Group()
LISTA_GLOBAL_SPRITES = pygame.sprite.Group()
class PLANETA(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("img/PLANETA.png").convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def update(self):
self.rect.y = self.rect.y + VELOCIDAD_PLANETAS
if self.rect.y > ALTURA_VENTANA:
LISTA_GLOBAL_SPRITES.remove(self)
LISTA_PLANETAS.remove(self)
self.kill()
class COHETE(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("img/COHETE.png").convert_alpha()
self.rect = self.image.get_rect()
self.rect.x...