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...

¿Qué significa el emblema de la profesión informática? (I)

Todas o muchas profesiones tienen un emblema que las representa simbólicamente y en el caso de la  informática: " es el establecido en la resolución de 11 de noviembre de 1977  para las titulaciones universitarias superiores de informática, y  está constituido por una figura representando en su parte central  un  núcleo toroidal de ferrita , atravesado por  hilos de lectura,  escritura e inhibición . El núcleo está rodeado por  dos ramas : una  de  laurel , como símbolo de recompensa, y la otra, de  olivo , como  símbolo de sabiduría. La  corona  será la  de la casa real  española,  y bajo el escudo se inscribirá el acrónimo de la organización. ". Veamos los diferentes elementos tomando como ejemplo el emblema del COIIE/EIIEO (Colegio Oficial de Ingenieros en Informática del País Vasco/ Euskadiko Informatikako Ingeniarien Elkargo Ofiziala ) . Pero no sólo el COIIE/EIIEO adopta el emblem...

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...