Ir al contenido principal

Python + Pygame (XXVIII): Tutorial - Salto de personajes (I)

Una de las cosas recurrentes a las que nos enfrentamos a la hora de programar un juego de tipo plataformas es la de implementar el salto del personaje.

Un ejemplo de videojuego de plataformas, en los que los personajes tienen que caminar, correr y escalar sobre una serie de plataformas con enemigos, mientras van recogiendo una serie de objetos para poder completar el juego,  es 'Donkey Kong', que fue el segundo videojuego de este tipo que se creó (1981).

Pues bien, ya que estoy entreteniéndome programando en python este videojuego, con programación orientada a objetos (POO) y utilizando la librería pygame, en éste y los siguientes tutoriales explico como he implementado el salto de 'Jumpman' (personaje que en posteriores juegos de Nintendo pasó a llamarse Mario).

En este primer tutorial pongo un script para implementar única y exclusivamente el salto, es decir, sin adornos (animación del personaje cuando anda y salta, sonidos, etc.), para centrarme sólo en el salto, y dejo para posteriores posts el completar este script con los detalles estéticos y otros adicionales.

Creo que los comentarios en el código hacen que éste sea autoexplicativo, por lo que los comentarios adicionales que incluyo son los que considero estrictamente necesarios para explicar algúnos aspectos importantes.

1.- Importar las librerías necesarias:

import pygame
import os

2.- Inicializar pygame, crear ventana e instancia del personaje:

def donkey_kong():
    # Inicializar módulos internos de pygame.
    pygame.init()
    # Establecer tamaño de la ventana.
    # Crear una superficie de visualización: la ventana, y centrarla.
    os.environ['SDL_VIDEO_CENTERED']='1'
    ventana = pygame.display.set_mode((674, 430))
    # Título de la ventana.
    pygame.display.set_caption('Donkey Kong')
    # Cargar la imagen del fondo y cambiar el formato de píxel para crear una
    # copia que se dibujará más rápidamente en la pantalla.
    fondo = pygame.image.load('recursos/imagenes/fondo_1.png')
    fondo = fondo.convert()
    # Posición inicial de Jumpman.
    x, y = 120, 365
    # Grupo individual para Jumpman y dibujarlo.
    grupo_jumpman = pygame.sprite.GroupSingle()
    jumpman = Jumpman(x, y)
    grupo_jumpman.add(jumpman)
    # Reloj para ejecutar el videojuego.
    reloj = pygame.time.Clock()
    # El juego se ejecutará a 60 frames por segundo.
    fps = 60

3.- Bucle principal del juego:

    while True:
        # Ejecutar el siguiente frame.
        reloj.tick(fps)
        # Escuchar cada uno de los eventos de la lista de eventos de Pygame.
        for evento in pygame.event.get():
            # Finalizar el juego cuando se produzca el evento de terminación
            # del programa (cierre de la ventana).
            if evento.type == pygame.QUIT:
                pygame.quit()
                return
        # Obtener la lista de teclas pulsadas.
        teclas = pygame.key.get_pressed()
        # Actualizar sprites.
        grupo_jumpman.update(teclas, 365)
        # Copiar la imagen de fondo de la ventana en el canvas que la mostrará.
        ventana.blit(fondo, (0, 0))
        # Dibujar sprites.
        grupo_jumpman.draw(ventana)
        # Mostrar la ventana dibujada (cambiar buffers, buffer pantalla a
        # disponible para dibujar y viceversa.
        pygame.display.flip()
    pygame.quit()

El manejo de las teclas pulsadas se hará mediante el método key.get_pressed(), que obtiene el estado de  todas las teclas del teclado.

4.- Clase del personaje:

class Jumpman(pygame.sprite.Sprite):
    # Jumpman (Mario) estará definido por la clase Jumpman, que hereda de la
    # clase sprite.Sprite de Pygame. Clase base para objetos visibles del
    # juego.
    def __init__(self, x, y):
        super().__init__()
        self.x, self.y  = x, y
        self.vel_x = 3
        self.vel_y = 0
        self.en_suelo = True
        self.en_salto = False
        self.sentido_salto = 0
        self.altura_salto = 10
        self.gravedad = 1
        self.image = pygame.image.load('recursos/imagenes/' +
                                       'jumpman_derecha_1.png').convert_alpha()
    def update(self, teclas, suelo_y):
        # El método update() se ejecutará en cada frame, y permite gestionar
        # las teclas pulsadas para producir los movimientos del personaje
        # (andar y saltar) y actualizar la posición de Jumpman.
        self.teclas = teclas
        self.suelo_y = suelo_y
        # Movimiento horizontal (tanto si el personaje está en el suelo como
        # si está saltando).
        if self.en_suelo:
            if self.teclas[pygame.K_LEFT]:
                self.image = pygame.image.load('recursos/imagenes/'       +
                                               'jumpman_izquierda_1.png') \
                                                       .convert_alpha()
                self.x -= self.vel_x
            elif self.teclas[pygame.K_RIGHT]:
                self.image = pygame.image.load('recursos/imagenes/'    +
                                               'jumpman_derecha_1.png') \
                                                       .convert_alpha()
                self.x += self.vel_x
        elif self.en_salto:
            self.x += self.sentido_salto * self.vel_x * 2
        # Salto.
        if self.teclas[pygame.K_SPACE] and self.en_suelo:
            self.vel_y = -self.altura_salto
            self.en_suelo = False
            self.en_salto = True
            # Guardar el sentido en el que se inicia el salto.
            if self.teclas[pygame.K_LEFT]:
                self.sentido_salto = -1
            elif self.teclas[pygame.K_RIGHT]:
                self.sentido_salto = 1
            else:
                self.sentido_salto = 0
        # Aplicar gravedad
        self.vel_y += self.gravedad
        self.y += self.vel_y
        # Colisión con el suelo
        if self.y >= self.suelo_y:
            self.y = self.suelo_y
            self.vel_y = 0
            self.en_suelo = True
            self.en_salto = False
        # Actualizar la posición de Jumpman.
        self.rect = self.image.get_rect(center=(self.x, self.y))

Hay que tener en cuenta que en el salto se pretende que cuando éste se inicie se complete a una misma velocidad hasta su finalización, es decir, el pulsar o levantar las teclas de movimiento horizontal durante el mismo no debe tener ningún efecto sobre su desarrolllo.

5.- Poniendo todo junto:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# SALTAR 1:
#
# Primera aproximación al salto de un personaje de videojuego.
#
# Autor: Mikel García Larragan.
# https://mikelgarcialarragan.blogspot.com/

import pygame
import os


class Jumpman(pygame.sprite.Sprite):
    # Jumpman (Mario) estará definido por la clase Jumpman, que hereda de la
    # clase sprite.Sprite de Pygame. Clase base para objetos visibles del
    # juego.
    def __init__(self, x, y):
        super().__init__()
        self.x, self.y  = x, y
        self.vel_x = 3
        self.vel_y = 0
        self.en_suelo = True
        self.en_salto = False
        self.sentido_salto = 0
        self.altura_salto = 10
        self.gravedad = 1
        self.image = pygame.image.load('recursos/imagenes/' +
                                       'jumpman_derecha_1.png').convert_alpha()

    def update(self, teclas, suelo_y):
        # El método update() se ejecutará en cada frame, y permite gestionar
        # las teclas pulsadas para producir los movimientos del personaje
        # (andar y saltar) y actualizar la posición de Jumpman.
        self.teclas = teclas
        self.suelo_y = suelo_y
        # Movimiento horizontal (tanto si el personaje está en el suelo como
        # si está saltando).
        if self.en_suelo:
            if self.teclas[pygame.K_LEFT]:
                self.image = pygame.image.load('recursos/imagenes/'       +
                                               'jumpman_izquierda_1.png') \
                                                       .convert_alpha()
                self.x -= self.vel_x
            elif self.teclas[pygame.K_RIGHT]:
                self.image = pygame.image.load('recursos/imagenes/'    +
                                               'jumpman_derecha_1.png') \
                                                       .convert_alpha()
                self.x += self.vel_x
        elif self.en_salto:
            self.x += self.sentido_salto * self.vel_x * 2
        # Salto.
        if self.teclas[pygame.K_SPACE] and self.en_suelo:
            self.vel_y = -self.altura_salto
            self.en_suelo = False
            self.en_salto = True
            # Guardar el sentido en el que se inicia el salto.
            if self.teclas[pygame.K_LEFT]:
                self.sentido_salto = -1
            elif self.teclas[pygame.K_RIGHT]:
                self.sentido_salto = 1
            else:
                self.sentido_salto = 0
        # Aplicar gravedad
        self.vel_y += self.gravedad
        self.y += self.vel_y
        # Colisión con el suelo
        if self.y >= self.suelo_y:
            self.y = self.suelo_y
            self.vel_y = 0
            self.en_suelo = True
            self.en_salto = False
        # Actualizar la posición de Jumpman.
        self.rect = self.image.get_rect(center=(self.x, self.y))


def donkey_kong():
    # Inicializar módulos internos de pygame.
    pygame.init()
    # Establecer tamaño de la ventana.
    # Crear una superficie de visualización: la ventana, y centrarla.
    os.environ['SDL_VIDEO_CENTERED']='1'
    ventana = pygame.display.set_mode((674, 430))
    # Título de la ventana.
    pygame.display.set_caption('Donkey Kong')
    # Cargar la imagen del fondo y cambiar el formato de píxel para crear una
    # copia que se dibujará más rápidamente en la pantalla.
    fondo = pygame.image.load('recursos/imagenes/fondo_1.png')
    fondo = fondo.convert()
    # Posición inicial de Jumpman.
    x, y = 120, 365
    # Grupo individual para Jumpman y dibujarlo.
    grupo_jumpman = pygame.sprite.GroupSingle()
    jumpman = Jumpman(x, y)
    grupo_jumpman.add(jumpman)
    # Reloj para ejecutar el videojuego.
    reloj = pygame.time.Clock()
    # El juego se ejecutará a 60 frames por segundo.
    fps = 60
    while True:
        # Ejecutar el siguiente frame.
        reloj.tick(fps)
        # Escuchar cada uno de los eventos de la lista de eventos de Pygame.
        for evento in pygame.event.get():
            # Finalizar el juego cuando se produzca el evento de terminación
            # del programa (cierre de la ventana).
            if evento.type == pygame.QUIT:
                pygame.quit()
                return
        # Obtener la lista de teclas pulsadas.
        teclas = pygame.key.get_pressed()
        # Actualizar sprites.
        grupo_jumpman.update(teclas, 365)
        # Copiar la imagen de fondo de la ventana en el canvas que la mostrará.
        ventana.blit(fondo, (0, 0))
        # Dibujar sprites.
        grupo_jumpman.draw(ventana)
        # Mostrar la ventana dibujada (cambiar buffers, buffer pantalla a
        # disponible para dibujar y viceversa.
        pygame.display.flip()
    pygame.quit()


if __name__ == '__main__':
    donkey_kong()
Si lo ejecutamos:
Quizás también te interese:

Comentarios

Entradas populares de este blog

Criptografía (I): cifrado Vigenère y criptoanálisis Kasiski

Hace unos días mi amigo Iñaki Regidor ( @Inaki_Regidor ), a quien dedico esta entrada :), compartió en las redes sociales un post titulado "Criptografía: el arte de esconder mensajes"  publicado en uno de los blogs de EiTB . En ese post se explican ciertos métodos clásicos para cifrar mensajes , entre ellos el cifrado de Vigenère , y , al final del mismo, se propone un reto consistente en descifrar un mensaje , lo que me ha animado a escribir este post sobre el método Kasiski  para atacar un cifrado polialfabético ( conociendo la clave descifrar el mensaje es muy fácil, pero lo que contaré en este post es la forma de hacerlo sin saberla ). El mensaje a descifrar es el siguiente: LNUDVMUYRMUDVLLPXAFZUEFAIOVWVMUOVMUEVMUEZCUDVSYWCIVCFGUCUNYCGALLGRCYTIJTRNNPJQOPJEMZITYLIAYYKRYEFDUDCAMAVRMZEAMBLEXPJCCQIEHPJTYXVNMLAEZTIMUOFRUFC Como ya he dicho el método de Vigenère es un sistema de sustitución polialfabético , lo que significa que, al contrario que en un sistema...

Criptografía (XXIII): cifrado de Hill (I)

En este post me propongo explicar de forma comprensible lo que he entendido sobre el cifrado de Hill , propuesto por el matemático Lester S. Hill , en 1929, y que se basa en emplear una matriz como clave  para cifrar un texto en claro y su inversa para descifrar el criptograma correspondiente . Hay tres cosas que me gustan de la criptografía clásica, además de que considero que ésta es muy didáctica a la hora de comprender los sistemas criptográficos modernos: la primera de ellas es que me "obliga" a repasar conceptos de matemáticas aprendidos hace mucho tiempo y, desgraciadamente, olvidados también hace demasiado tiempo, y, por consiguiente, que, como dice  Dani , amigo y coautor de este blog, me "obliga" a hacer "gimnasia mental"; la segunda es que, en la mayoría de las ocasiones, pueden cifrarse y descifrase los mensajes, e incluso realizarse el criptoanálisis de los criptogramas, sin más que un simple lápiz y papel, es decir, para mi es como un pasat...

Criptografía (CLXXXIV): Soluciones Retos criptografía de CyberOlympics 2017

En este post pongo las soluciones a los retos de  criptografía que he ido resolviendo de la edición del año 2017 de CyberOlympics , competición en modalidad  'on-line' , estilo  'Capture the Flag'  y formato  'Jeopardy'  dirigida a centros educativos y organizada por el Instituto Nacional de Ciberseguridad (INCIBE) en el marco de la actividad llamada CyberCamp. En esta edición la mayoría de los retos presentaron un  nivel  de dificultad bajo  ( ★ ★ ☆☆☆ ) , lo que entiendo adecuado por el colectivo al que van dirigidos. Las soluciones al resto de desafíos de criptografía de esta edición, cuyos archivos asociados tenga (no me han pasado todos) y que consiga resolver, las pondré en otra entrada. Reto 1 (Criptografía) : Enunciado : Todas las mañanas cuando me despierto, me miro en el espejo y no entiendo lo que veo. Hoy me he levantado dando un salto mortal y no voy a apartarme de mi “otro yo” hasta que no descubra el mensaje. Parece q...