Construir un juego de laberinto con Pygame

PythonBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

Introducción

En este proyecto, crearemos un juego de laberinto utilizando la librería Pygame en Python. El juego consiste en guiar a un jugador a través de un laberinto para recoger elementos de comida mientras evita los muros. Dividiremos el proceso de desarrollo en múltiples pasos para hacerlo más fácil de entender y seguir.

👀 Vista previa

Captura de pantalla de vista previa del juego de laberinto

🎯 Tareas

En este proyecto, aprenderá:

  • Cómo configurar el entorno del juego utilizando Pygame
  • Cómo crear el laberinto utilizando celdas y muros
  • Cómo agregar elementos de comida para que el jugador los recoja
  • Cómo implementar el movimiento del jugador y la detección de colisiones
  • Cómo manejar la lógica del juego, incluyendo la puntuación y las condiciones de fin de juego
  • Cómo llevar un registro del récord del jugador
  • Cómo mostrar estadísticas del juego como el tiempo, la puntuación y el récord en la pantalla

🏆 Logros

Después de completar este proyecto, podrá:

  • Utilizar la librería Pygame para el desarrollo de juegos
  • Aplicar conceptos de programación orientada a objetos para crear elementos de juego
  • Demostrar pensamiento algorítmico y habilidades de resolución de problemas para la generación de laberintos
  • Manejar el procesamiento de eventos y la entrada del jugador
  • Implementar la detección de colisiones y la mecánica de movimiento en un entorno de juego
  • Administrar el manejo de archivos para almacenar y recuperar registros de juego
  • Mostrar estadísticas e información del juego en la pantalla
Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel intermedio con una tasa de finalización del 67%. Ha recibido una tasa de reseñas positivas del 50% por parte de los estudiantes.

Configurar el entorno

Primero, crearemos los archivos del proyecto para el juego de Laberinto.

cd ~/proyecto
touch laberinto.py
sudo pip install pygame

En este paso, configuraremos el entorno de Pygame y definiremos constantes.

import pygame
from random import choice, randrange

## Constantes para las dimensiones de la pantalla y el tamaño de las baldosas
RES = ANCHO, ALTO = 1202, 902
TILE = 100
columnas, filas = ANCHO // TILE, ALTO // TILE

## El resto de su código irá aquí...

En este paso:

  • Importamos las bibliotecas necesarias (Pygame y random).
  • Definimos constantes para las dimensiones de la pantalla y el tamaño de las baldosas.
  • Inicializamos Pygame y configuramos la ventana del juego.
  • Cargamos las imágenes de fondo para el juego.
✨ Revisar Solución y Practicar

Creando la clase Celda

En este paso, definiremos la clase Celda para representar las celdas del laberinto.

## Define una clase para representar celdas en el laberinto
class Celda:
    def __init__(self, x, y):
        self.x, self.y = x, y
        ## Las paredes representan los límites de la celda
        self.paredes = {"arriba": True, "derecha": True, "abajo": True, "izquierda": True}
        self.visitada = False
        self.grosor = 4

    ## Dibuja las paredes de la celda
    def dibujar(self, sc):
        x, y = self.x * TILE, self.y * TILE
        if self.paredes["arriba"]:
            pygame.draw.line(
                sc, pygame.Color("naranja oscuro"), (x, y), (x + TILE, y), self.grosor
            )
        if self.paredes["derecha"]:
            pygame.draw.line(
                sc,
                pygame.Color("naranja oscuro"),
                (x + TILE, y),
                (x + TILE, y + TILE),
                self.grosor,
            )
        if self.paredes["abajo"]:
            pygame.draw.line(
                sc,
                pygame.Color("naranja oscuro"),
                (x + TILE, y + TILE),
                (x, y + TILE),
                self.grosor,
            )
        if self.paredes["izquierda"]:
            pygame.draw.line(
                sc, pygame.Color("naranja oscuro"), (x, y + TILE), (x, y), self.grosor
            )

    ## Obtiene los rectángulos que representan cada pared de la celda
    def obtener_rectangulos(self):
        rectangulos = []
        x, y = self.x * TILE, self.y * TILE
        if self.paredes["arriba"]:
            rectangulos.append(pygame.Rect((x, y), (TILE, self.grosor)))
        if self.paredes["derecha"]:
            rectangulos.append(pygame.Rect((x + TILE, y), (self.grosor, TILE)))
        if self.paredes["abajo"]:
            rectangulos.append(pygame.Rect((x, y + TILE), (TILE, self.grosor)))
        if self.paredes["izquierda"]:
            rectangulos.append(pygame.Rect((x, y), (self.grosor, TILE)))
        return rectangulos

    ## Verifica si existe una celda vecina
    def verificar_celda(self, x, y):
        encontrar_indice = lambda x, y: x + y * columnas
        if x < 0 or x > columnas - 1 or y < 0 or y > filas - 1:
            return False
        return self.celdas_grid[encontrar_indice(x, y)]

    ## Obtiene las celdas vecinas que no han sido visitadas
    def verificar_vecinos(self, celdas_grid):
        self.celdas_grid = celdas_grid
        vecinos = []
        arriba = self.verificar_celda(self.x, self.y - 1)
        derecha = self.verificar_celda(self.x + 1, self.y)
        abajo = self.verificar_celda(self.x, self.y + 1)
        izquierda = self.verificar_celda(self.x - 1, self.y)
        if arriba and not arriba.visitada:
            vecinos.append(arriba)
        if derecha and not derecha.visitada:
            vecinos.append(derecha)
        if abajo and not abajo.visitada:
            vecinos.append(abajo)
        if izquierda and not izquierda.visitada:
            vecinos.append(izquierda)
        return choice(vecinos) if vecinos else False

## El resto de su código irá aquí...

En este paso:

  • Definimos la clase Celda con sus propiedades y métodos para dibujar paredes y verificar vecinos.
✨ Revisar Solución y Practicar

Eliminando paredes y generando el laberinto

En este paso, crearemos funciones para eliminar paredes y generar el laberinto.

## Función para eliminar las paredes entre dos celdas adyacentes
def eliminar_paredes(actual, siguiente):
    dx = actual.x - siguiente.x
    if dx == 1:
        actual.paredes["izquierda"] = False
        siguiente.paredes["derecha"] = False
    elif dx == -1:
        actual.paredes["derecha"] = False
        siguiente.paredes["izquierda"] = False
    dy = actual.y - siguiente.y
    if dy == 1:
        actual.paredes["arriba"] = False
        siguiente.paredes["abajo"] = False
    elif dy == -1:
        actual.paredes["abajo"] = False
        siguiente.paredes["arriba"] = False


## Función para generar el laberinto
def generar_laberinto():
    celdas_grid = [Celda(col, fila) for fila in range(filas) for col in range(columnas)]
    celda_actual = celdas_grid[0]
    array = []
    contador_rompimiento = 1

    while contador_rompimiento!= len(celdas_grid):
        celda_actual.visitada = True
        siguiente_celda = celda_actual.verificar_vecinos(celdas_grid)
        if siguiente_celda:
            siguiente_celda.visitada = True
            contador_rompimiento += 1
            array.append(celda_actual)
            eliminar_paredes(celda_actual, siguiente_celda)
            celda_actual = siguiente_celda
        elif array:
            celda_actual = array.pop()
    return celdas_grid

## El resto de su código irá aquí...

En este paso:

  • Definimos la función eliminar_paredes para eliminar las paredes entre celdas adyacentes.
  • Creamos la función generar_laberinto para generar el laberinto utilizando un algoritmo de búsqueda en profundidad.
✨ Revisar Solución y Practicar

Agregando comida al juego

En este paso, crearemos una clase Comida para agregar elementos de comida al juego.

## Clase para representar la comida en el juego
class Comida:
    def __init__(self):
        ## Cargar la imagen de la comida
        self.img = pygame.image.load("img/comida.png").convert_alpha()
        self.img = pygame.transform.scale(self.img, (TILE - 10, TILE - 10))
        self.rect = self.img.get_rect()
        self.set_pos()

    ## Establecer la posición de la comida de forma aleatoria
    def set_pos(self):
        self.rect.topleft = randrange(columnas) * TILE + 5, randrange(filas) * TILE + 5

    ## Dibujar la comida en la pantalla
    def dibujar(self):
        superficie_juego.blit(self.img, self.rect)

## El resto de su código irá aquí...

En este paso:

  • Definimos la clase Comida con métodos para establecer la posición y dibujar los elementos de comida.
✨ Revisar Solución y Practicar

Movimiento del jugador y detección de colisiones

En este paso, configuraremos los controles del jugador, su movimiento y la detección de colisiones.

## Verifica si el jugador choca con las paredes
def es_colision(x, y):
    rect_tmp = rect_jugador.move(x, y)
    if rect_tmp.collidelist(lista_colisiones_paredes) == -1:
        return False
    return True

## El resto de su código irá aquí...

En este paso:

  • Definimos la función es_colision para verificar si el jugador choca con las paredes.
✨ Revisar Solución y Practicar

Juego y Puntuación

En este paso, implementaremos la lógica del juego, incluyendo comer comida y puntuar.

## Verifica si el jugador ha comido alguna comida
def comer_comida():
    for comida in lista_comida:
        if rect_jugador.collidepoint(comida.rect.center):
            comida.set_pos()
            return True
    return False


## Verifica si el juego ha terminado (se agota el tiempo)
def esta_terminado_el_juego():
    global tiempo, puntuacion, record, FPS
    if tiempo < 0:
        pygame.time.wait(700)
        rect_jugador.center = TILE // 2, TILE // 2
        [comida.set_pos() for comida in lista_comida]
        establecer_record(record, puntuacion)
        record = obtener_record()
        tiempo, puntuacion, FPS = 60, 0, 60

## El resto de su código irá aquí...

En este paso:

  • Definimos la función comer_comida para verificar si el jugador ha comido alguna comida.
  • Creamos la función esta_terminado_el_juego para verificar si el juego ha terminado cuando se agota el tiempo.
✨ Revisar Solución y Practicar

Manejo de registros

En este paso, implementaremos la conservación de registros para el juego.

## Función para obtener el registro actual de un archivo
def obtener_registro():
    try:
        with open("registro") as f:
            return f.readline()
    except FileNotFoundError:
        with open("registro", "w") as f:
            f.write("0")
            return "0"

## Función para establecer y actualizar el registro en un archivo
def establecer_registro(registro, puntuación):
    rec = max(int(registro), puntuación)
    with open("registro", "w") as f:
        f.write(str(rec))

## El resto de su código irá aquí...

En este paso:

  • Definimos funciones para recuperar el registro actual de un archivo y actualizarlo.
✨ Revisar Solución y Practicar

Inicialización del juego

En este paso, realizaremos las tareas de inicialización del juego.

## Inicializar Pygame y configurar la ventana del juego
FPS = 60
pygame.init()
superficie_juego = pygame.Surface(RES)
superficie = pygame.display.set_mode((ANCHO + 300, ALTO))
reloj = pygame.time.Clock()

## Cargar las imágenes de fondo
fondo_juego = pygame.image.load("img/fondo_1.jpg").convert()
fondo = pygame.image.load("img/fondo_principal.jpg").convert()

## Generar el laberinto
laberinto = generar_laberinto()

## Configuración del jugador
velocidad_jugador = 5
imagen_jugador = pygame.image.load("img/0.png").convert_alpha()
imagen_jugador = pygame.transform.scale(
    imagen_jugador, (TILE - 2 * laberinto[0].grosor, TILE - 2 * laberinto[0].grosor)
)
rect_jugador = imagen_jugador.get_rect()
rect_jugador.center = TILE // 2, TILE // 2
direcciones = {
    "a": (-velocidad_jugador, 0),
    "d": (velocidad_jugador, 0),
    "w": (0, -velocidad_jugador),
    "s": (0, velocidad_jugador),
}
teclas = {"a": pygame.K_LEFT, "d": pygame.K_RIGHT, "w": pygame.K_UP, "s": pygame.K_DOWN}
direccion = (0, 0)

## Configuración de la comida
lista_comida = [Comida() for i in range(3)]

## Crear una lista de rectángulos que representan las paredes para la detección de colisiones
lista_colisiones_paredes = sum([celda.get_rects() for celda in laberinto], [])

## Temporizador, puntuación y récord
pygame.time.set_timer(pygame.USEREVENT, 1000)
tiempo = 60
puntuacion = 0
record = obtener_record()

## Fuentes
fuente = pygame.font.SysFont("Impact", 150)
fuente_texto = pygame.font.SysFont("Impact", 80)

## El resto de su código irá aquí...

En este paso:

  • Realizamos varias tareas de inicialización, incluyendo configurar Pygame, cargar imágenes, generar el laberinto e inicializar variables relacionadas con el jugador y la comida.
✨ Revisar Solución y Practicar

Bucle principal del juego

En este paso, configuraremos el bucle principal del juego y mostraremos los elementos del juego.

## Bucle principal del juego
while True:
    ## Dibuja las imágenes de fondo
    superficie.blit(fondo, (ANCHO, 0))
    superficie.blit(superficie_juego, (0, 0))
    superficie_juego.blit(fondo_juego, (0, 0))

    for evento in pygame.event.get():
        if evento.type == pygame.QUIT:
            exit()
        if evento.type == pygame.USEREVENT:
            tiempo -= 1

    ## Maneja los controles y el movimiento del jugador
    tecla_presionada = pygame.key.get_pressed()
    for tecla, valor_tecla in teclas.items():
        if tecla_presionada[valor_tecla] and not es_colision(*direcciones[tecla]):
            direccion = direcciones[tecla]
            break
    if not es_colision(*direccion):
        rect_jugador.move_ip(direccion)

    ## Dibuja el laberinto
    [celda.dibujar(superficie_juego) for celda in laberinto]

    ## Juego: Verifica si el jugador ha comido comida y si el juego ha terminado
    if comer_comida():
        FPS += 10
        puntuacion += 1
    esta_terminado_el_juego()

    ## Dibuja al jugador
    superficie_juego.blit(imagen_jugador, rect_jugador)

    ## Dibuja los elementos de comida
    [comida.dibujar() for comida in lista_comida]

    ## El resto de su código irá aquí...

En este paso:

  • Configuramos el bucle principal del juego que maneja los eventos, el movimiento del jugador y la representación gráfica del juego.
✨ Revisar Solución y Practicar

Mostrando Estadísticas del Juego

En este paso, mostraremos las estadísticas del juego en la pantalla.

    ## Dibuja las estadísticas del juego
    superficie.blit(
        fuente_texto.render("TIEMPO", True, pygame.Color("cian"), True), (ANCHO + 70, 30)
    )
    superficie.blit(fuente.render(f"{tiempo}", True, pygame.Color("cian")), (ANCHO + 70, 130))
    superficie.blit(
        fuente_texto.render("puntuación:", True, pygame.Color("verde bosque"), True),
        (ANCHO + 50, 350),
    )
    superficie.blit(
        fuente.render(f"{puntuacion}", True, pygame.Color("verde bosque")), (ANCHO + 70, 430)
    )
    superficie.blit(
        fuente_texto.render("récord:", True, pygame.Color("magenta"), True),
        (ANCHO + 30, 620),
    )
    superficie.blit(
        fuente.render(f"{record}", True, pygame.Color("magenta")), (ANCHO + 70, 700)
    )

    pygame.display.flip()
    reloj.tick(FPS)

En este paso:

  • Utilizamos fuentes para mostrar información relacionada con el juego, como el tiempo, la puntuación y el récord.
  • Utilizamos el método blit() para dibujar el texto en la pantalla.
  • Utilizamos el método flip() para actualizar la pantalla.
✨ Revisar Solución y Practicar

Ejecutar el juego

Ahora que hemos completado todos los pasos, podemos ejecutar el juego del laberinto con el siguiente comando:

cd ~/proyecto
python laberinto.py
Captura de pantalla de la ejecución del juego del laberinto
✨ Revisar Solución y Practicar

Resumen

En este proyecto, hemos dividido el proceso de construir un juego de laberinto utilizando Pygame en diez pasos claros y manejables. Aprenderás cómo configurar el entorno del juego, crear las celdas del laberinto, generar el laberinto, manejar el movimiento del jugador y la detección de colisiones, implementar la dinámica del juego y la puntuación, gestionar los registros y mucho más. Siguiendo estos pasos, podrás crear un juego de laberinto completamente funcional en Python.