En el anterior post puse un script con un código que puede servir (se puede hacer de otras muchas formas - utilizando la función seno, etc.) para implementar el salto del personaje de un videojuego 2D, y en éste lo completo con algunos aspectos adicionales (animación del personaje al andar y saltar, se incluyen algunos sonidos y detección de la colisión entre el personaje y los tramos de la plataforma utilizando sprites para estos últimos).
El código incluido y modificado más relevante es el siguiente (al igual que en el anterior post, los comentarios en el código hacen que éste sea autoexplicativo, por lo que me limitaré a realizar sólo nuevos comentarios que puedan aportar algo a la explicación):
1.- Inicializar determinados aspectos:
# Cargar archivos de sonidos.
sonido_andar = pygame.mixer.Sound(
'recursos/sonidos/andar.wav')
sonido_saltar = pygame.mixer.Sound(
'recursos/sonidos/saltar.wav')
# Crear canal para sonidos.
canal1 = pygame.mixer.Channel(0)
# Grupo de sprites. Agrupamos los tramos del suelo usando la clase
# sprite.Group de Pygame para hacer que dicha clase se encargue de
# actualizarlos y dibujarlos, sin necesidad de hacerlo manualmente.
grupo_tramos = pygame.sprite.Group()
# Colocar los tramos (el suelo del piso 0 en la pantalla).
for num_tramo in range(14):
tramo = Tramo(24 + 48 * num_tramo, 396)
grupo_tramos.add(tramo)
# Variables booleanas que indican si Jumpman se encuentra en el suelo o
# está saltando, respectivamente.
en_suelo = True
en_salto = False
# Sentido del movimiento de Jumpman, tanto cuando anda como cuando salta.
sentido = 'DERECHA'
# Velocidad de movimiento en ambos ejes.
vel_x, vel_y = 3, 0
# Variables correspondientes al salto.
sentido_salto = 0
altura_salto = 10
gravedad = 1
# Establecer el tiempo transcurrido (valor de un contador) desde el inicio
# del movimiento de Jumpman, tanto cuando está andando como cuando salta, y
# el que debe transcurrir hasta su finalización.
contador_inicio_mover = 0
contador_fin_mover = 10
# Variable booleana que indica si se debe finalizar de mostrar el
# movimiento de Jumpman (andar o saltar).
finalizar_mover = False
Es decir, básicamente, se incluye el código necesario para: cargar los archivos de sonido y crear el canal para reproducirlos, colocar los tramos (sprites) del piso 0 - instancias de la clase 'Tramo' , que veremos más adelante - y añadirlos al grupo correspondiente, e inicializar una serie de variables relativas al movimiento.
Sonidos:
Andar:
Saltar:
2.- Manejo de teclas pulsadas:
# Obtener la lista de teclas pulsadas y gestión de las mismas. teclas = pygame.key.get_pressed() # Movimiento horizontal (tanto si Jumpman está en el suelo como si está # saltando). if en_suelo: vel_x = 3 if teclas[pygame.K_LEFT]: canal1.play(sonido_andar) sentido = 'IZQUIERDA' jumpman.mover('ANDAR', sentido, 2, -vel_x, 0) finalizar_mover = True elif teclas[pygame.K_RIGHT]: canal1.play(sonido_andar) sentido = 'DERECHA' jumpman.mover('ANDAR', sentido, 2, vel_x, 0) finalizar_mover = True elif en_salto: jumpman.mover('SALTAR', sentido, 1, sentido_salto * 6, vel_y) # Salto. if teclas[pygame.K_SPACE] and en_suelo: canal1.play(sonido_saltar) vel_x = 0 vel_y = -altura_salto jumpman.mover('SALTAR', sentido, 1, vel_x, -altura_salto) en_suelo = False en_salto = True # Guardar el sentido en el que se inicia el salto. if teclas[pygame.K_LEFT]: sentido_salto = -1 elif teclas[pygame.K_RIGHT]: sentido_salto = 1 else: sentido_salto = 0 if en_salto: # Aplicar gravedad. vel_y += gravedad # Comprobar si ha aterrizado (colisiona con un tramo del piso 0). tramos_colision = pygame.sprite.groupcollide(grupo_jumpman, grupo_tramos, False, False) if len(tramos_colision) > 0: vel_x = 0 # Colisión con el suelo. jumpman.mover('SALTAR', sentido, 1, vel_x, tramos_colision[grupo_jumpman.sprites()[0]][0]. rect.top - grupo_jumpman.sprites()[0].rect.bottom - tramo.alto_tramo // 2 - 1) jumpman.finalizar_mover() vel_y = 0 en_suelo = True en_salto = False # Animar movimientos de Jumpman. if finalizar_mover: if contador_inicio_mover > contador_fin_mover: jumpman.finalizar_mover() contador_inicio_mover = 0 finalizar_mover = False else: contador_inicio_mover += 1
La gestión de las teclas pulsadas se traslada del método update() de la clase 'Jumpman' al bucle principal del juego, y se incluyen la gestión de las colisiones de 'Jumpman' con los tramos del piso 0 (aterrizaje después del salto) y la animación de los movimientos de 'Jumpman'.
3.- Clases:
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.image = pygame.image.load('recursos/imagenes/' +
'jumpman_derecha_1.png').convert_alpha()
def mover(self, movimiento, sentido, paso, vel_x, vel_y):
# Método que permite que Jumpman se mueva (ande o salte) en el sentido
# indicado.
self.movimiento = movimiento
self.sentido = sentido
self.paso = paso
self.x += vel_x
self.y += vel_y
if self.movimiento == 'ANDAR':
self.image = pygame.image.load('recursos/imagenes/' +
'jumpman_' + self.sentido + '_' +
str(self.paso) + '.png') \
.convert_alpha()
elif self.movimiento == 'SALTAR':
self.image = pygame.image.load('recursos/imagenes/' +
'jumpman_saltar_' +
self.sentido + '.png') \
.convert_alpha()
def finalizar_mover(self):
# Método que permite completar el movimiento de Jumpman, tanto cuando
# anda como cuando salta, mostrando la imagen correspondiente a Jumpman
# parado.
self.image = pygame.image.load('recursos/imagenes/' +
'jumpman_' + self.sentido + '_1.png') \
.convert_alpha()
def update(self):
# El método update() se ejecutará en cada frame y permite actualizar la
# posición de Jumpman.
self.rect = self.image.get_rect(center=(self.x, self.y))
class Tramo(pygame.sprite.Sprite):
# Los tramos del suelo por donde anda y salta Jumpman estarán definidos por
# la clase Tramo, 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.image = pygame.image.load('recursos/imagenes/tramo.png') \
.convert_alpha()
self.alto_tramo = self.image.get_height()
def update(self):
# El método update() se ejecutará en cada frame, y permite actualizar
# los tramos.
self.rect = self.image.get_rect(center=(self.x, self.y))
Las clases 'Jumpman' y 'Tramo' son las encargadas de gestionar las imágenes y de actualizar su posición.
4.- Poniendo todo junto:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# SALTAR 2:
#
# Segunda 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.image = pygame.image.load('recursos/imagenes/' +
'jumpman_derecha_1.png').convert_alpha()
def mover(self, movimiento, sentido, paso, vel_x, vel_y):
# Método que permite que Jumpman se mueva (ande o salte) en el sentido
# indicado.
self.movimiento = movimiento
self.sentido = sentido
self.paso = paso
self.x += vel_x
self.y += vel_y
if self.movimiento == 'ANDAR':
self.image = pygame.image.load('recursos/imagenes/' +
'jumpman_' + self.sentido + '_' +
str(self.paso) + '.png') \
.convert_alpha()
elif self.movimiento == 'SALTAR':
self.image = pygame.image.load('recursos/imagenes/' +
'jumpman_saltar_' +
self.sentido + '.png') \
.convert_alpha()
def finalizar_mover(self):
# Método que permite completar el movimiento de Jumpman, tanto cuando
# anda como cuando salta, mostrando la imagen correspondiente a Jumpman
# parado.
self.image = pygame.image.load('recursos/imagenes/' +
'jumpman_' + self.sentido + '_1.png') \
.convert_alpha()
def update(self):
# El método update() se ejecutará en cada frame y permite actualizar la
# posición de Jumpman.
self.rect = self.image.get_rect(center=(self.x, self.y))
class Tramo(pygame.sprite.Sprite):
# Los tramos del suelo por donde anda y salta Jumpman estarán definidos por
# la clase Tramo, 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.image = pygame.image.load('recursos/imagenes/tramo.png') \
.convert_alpha()
self.alto_tramo = self.image.get_height()
def update(self):
# El método update() se ejecutará en cada frame, y permite actualizar
# los tramos.
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_2.png')
fondo = fondo.convert()
# Cargar archivos de sonidos.
sonido_andar = pygame.mixer.Sound(
'recursos/sonidos/andar.wav')
sonido_saltar = pygame.mixer.Sound(
'recursos/sonidos/saltar.wav')
# Crear canal para sonidos.
canal1 = pygame.mixer.Channel(0)
# Grupo de sprites. Agrupamos los tramos del suelo usando la clase
# sprite.Group de Pygame para hacer que dicha clase se encargue de
# actualizarlos y dibujarlos, sin necesidad de hacerlo manualmente.
grupo_tramos = pygame.sprite.Group()
# Colocar los tramos (el suelo del piso 0 en la pantalla).
for num_tramo in range(14):
tramo = Tramo(24 + 48 * num_tramo, 396)
grupo_tramos.add(tramo)
# 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)
# Variables booleanas que indican si Jumpman se encuentra en el suelo o
# está saltando, respectivamente.
en_suelo = True
en_salto = False
# Sentido del movimiento de Jumpman, tanto cuando anda como cuando salta.
sentido = 'DERECHA'
# Velocidad de movimiento en ambos ejes.
vel_x, vel_y = 3, 0
# Variables correspondientes al salto.
sentido_salto = 0
altura_salto = 10
gravedad = 1
# Establecer el tiempo transcurrido (valor de un contador) desde el inicio
# del movimiento de Jumpman, tanto cuando está andando como cuando salta, y
# el que debe transcurrir hasta su finalización.
contador_inicio_mover = 0
contador_fin_mover = 10
# Variable booleana que indica si se debe finalizar de mostrar el
# movimiento de Jumpman (andar o saltar).
finalizar_mover = False
# 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 y gestión de las mismas.
teclas = pygame.key.get_pressed()
# Movimiento horizontal (tanto si Jumpman está en el suelo como si está
# saltando).
if en_suelo:
vel_x = 3
if teclas[pygame.K_LEFT]:
canal1.play(sonido_andar)
sentido = 'IZQUIERDA'
jumpman.mover('ANDAR', sentido, 2, -vel_x, 0)
finalizar_mover = True
elif teclas[pygame.K_RIGHT]:
canal1.play(sonido_andar)
sentido = 'DERECHA'
jumpman.mover('ANDAR', sentido, 2, vel_x, 0)
finalizar_mover = True
elif en_salto:
jumpman.mover('SALTAR', sentido, 1, sentido_salto * 6, vel_y)
# Salto.
if teclas[pygame.K_SPACE] and en_suelo:
canal1.play(sonido_saltar)
vel_x = 0
vel_y = -altura_salto
jumpman.mover('SALTAR', sentido, 1, vel_x, -altura_salto)
en_suelo = False
en_salto = True
# Guardar el sentido en el que se inicia el salto.
if teclas[pygame.K_LEFT]:
sentido_salto = -1
elif teclas[pygame.K_RIGHT]:
sentido_salto = 1
else:
sentido_salto = 0
if en_salto:
# Aplicar gravedad.
vel_y += gravedad
# Comprobar si ha aterrizado (colisiona con un tramo del piso 0).
tramos_colision = pygame.sprite.groupcollide(grupo_jumpman,
grupo_tramos,
False, False)
if len(tramos_colision) > 0:
vel_x = 0
# Colisión con el suelo.
jumpman.mover('SALTAR', sentido, 1, vel_x,
tramos_colision[grupo_jumpman.sprites()[0]][0].
rect.top -
grupo_jumpman.sprites()[0].rect.bottom -
tramo.alto_tramo // 2 - 1)
jumpman.finalizar_mover()
vel_y = 0
en_suelo = True
en_salto = False
# Animar movimientos de Jumpman.
if finalizar_mover:
if contador_inicio_mover > contador_fin_mover:
jumpman.finalizar_mover()
contador_inicio_mover = 0
finalizar_mover = False
else:
contador_inicio_mover += 1
# Actualizar sprites.
grupo_tramos.update()
grupo_jumpman.update()
# Copiar la imagen de fondo de la ventana en el canvas que la mostrará.
ventana.blit(fondo, (0, 0))
# Dibujar sprites.
grupo_tramos.draw(ventana)
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:
Comentarios
Publicar un comentario