"""
Sample Python/Pygame Programs
Simpson College Computer Science
http://programarcadegames.com/
http://simpson.edu/computer-science/
De:
http://programarcadegames.com/python_examples/f.php?file=plataforma_scroller.py
Vídeo explicativo: http://youtu.be/QplXBw_NK5Y
Parte de la serie:
http://programarcadegames.com/python_examples/f.php?file=move_with_walls_example.py
http://programarcadegames.com/python_examples/f.php?file=maze_runner.py
http://programarcadegames.com/python_examples/f.php?file=plataforma_saltarer.py
http://programarcadegames.com/python_examples/f.php?file=plataforma_scroller.py
http://programarcadegames.com/python_examples/f.php?file=plataforma_moving.py
http://programarcadegames.com/python_examples/sprite_sheets/
"""
import pygame
"""
Constantes globales
"""
# Colores
NEGRO = (0, 0, 0)
BLANCO = (255, 255, 255)
AZUL = (0, 0, 255)
ROJO = (255, 0, 0)
VERDE = (0, 255, 0)
# Dimensiones de la pantalla
LARGO_PANTALLA = 800
ALTO_PANTALLA = 600
class Protagonista(pygame.sprite.Sprite):
""" Esta clase representa la barra inferior que controla el protagonista. """
# -- Métodos
def __init__(self):
""" Función Constructor"""
# Llama al constructor padre
super().__init__()
# Crea una imagen del bloque y lo rellena con color rojo.
# También podríamos usar una imagen guardada en disco
largo = 40
alto = 60
self.image = pygame.Surface([largo, alto])
self.image.fill(ROJO)
# Establecemos una referencia hacia la imagen rectangular.
self.rect = self.image.get_rect()
# Establecemos el vector velocidad del protagonista
self.cambio_x = 0
self.cambio_y = 0
# Lista de todos los sprites contra los que podemos botar
self.nivel = None
def update(self):
""" Desplazamos al protagonista. """
# Gravedad
self.calc_grav()
# Desplazar izquierda/derecha
self.rect.x += self.cambio_x
# Comprobamos si hemos chocado contra algo
lista_impactos_bloques = pygame.sprite.spritecollide(self, self.nivel.listade_plataformas, False)
for bloque in lista_impactos_bloques:
# Si nos estamos desplazando hacia la derecha, hacemos que nuestro lado derecho sea el lado
# izquierdo del objeto que hemos tocado
if self.cambio_x > 0:
self.rect.right = bloque.rect.left
elif self.cambio_x < 0:
# En caso contrario, si nos desplazamos hacia la izquierda, hacemos lo opuesto.
self.rect.left = bloque.rect.right
# Desplazar arriba/izquierda
self.rect.y += self.cambio_y
# Comprobamos si hemos chocado contra algo
lista_impactos_bloques = pygame.sprite.spritecollide(self, self.nivel.listade_plataformas, False)
for bloque in lista_impactos_bloques:
#Restablecemos nuestra posición basándonos en la parte superior/inferior del objeto.
if self.cambio_y > 0:
self.rect.bottom = bloque.rect.top
elif self.cambio_y < 0:
self.rect.top = bloque.rect.bottom
# Detenemos nuestro movimiento vertical
self.cambio_y = 0
def calc_grav(self):
""" Calculamos el efecto de la gravedad. """
if self.cambio_y == 0:
self.cambio_y = 1
else:
self.cambio_y += .35
#Observamos si nos encontramos sobre el suelo.
if self.rect.y >= ALTO_PANTALLA - self.rect.height and self.cambio_y >= 0:
self.cambio_y = 0
self.rect.y = ALTO_PANTALLA - self.rect.height
def saltar(self):
""" Llamado cuando el usuario pulsa el botón de 'saltar'. """
# Descendemos un poco y observamos si hay una plataforma debajo nuestro.
# Descendemos 2 píxels (con una plataforma que está descendiendo, no funciona bien
# si solo descendemos uno).
self.rect.y += 2
lista_impactos_plataforma = pygame.sprite.spritecollide(self, self.nivel.listade_plataformas, False)
self.rect.y -= 2
# Si está listo para saltar, aumentamos nuestra velocidad hacia arriba
if len(lista_impactos_plataforma) > 0 or self.rect.bottom >= ALTO_PANTALLA:
self.cambio_y = -10
# Movimiento controlado por el protagonista:
def ir_izquierda(self):
""" Es llamado cuando el usuario pulsa la flecha izquierda """
self.cambio_x = -6
def ir_derecha(self):
""" Es llamado cuando el usuario pulsa la flecha derecha """
self.cambio_x = 6
def stop(self):
""" Es llamado cuando el usuario abandona el teclado """
self.cambio_x = 0
class Plataforma(pygame.sprite.Sprite):
""" Plataforma sobre la que el usuario puede saltar """
def __init__(self, largo, alto ):
""" Constructor Plataforma.Asume su construcción cuando el usuario le haya pasado
un array de 5 números, tal como se ha definido al principio de este código.. """
super().__init__()
self.image = pygame.Surface([largo, alto])
self.image.fill(VERDE)
self.rect = self.image.get_rect()
class Nivel():
""" Esta es una super clase genérica usada para definir un nivel.
Crea una clase hija para cada nivel con una info específica. """
def __init__(self, protagonista):
""" Constructor. Requerido para cuando las plataformas
que se desplazan colisionan con el protagonista. """
self.listade_plataformas = pygame.sprite.Group()
self.listade_enemigos = pygame.sprite.Group()
self.protagonista = protagonista
# Cuán lejos se ha desplazado a la izquierda/derecha el escenario
self.desplazar_escenario = 0
# Actualizamos todo en este nivel
def update(self):
""" Actualizamos todo en este nivel."""
self.listade_plataformas.update()
self.listade_enemigos.update()
def draw(self, pantalla):
""" Dibujamos todo en este nivel. """
# Dibujamos el fondo
pantalla.fill(AZUL)
# Dibujamos todas las listas de sprites que tengamos
self.listade_plataformas.draw(pantalla)
self.listade_enemigos.draw(pantalla)
def escenario_desplazar(self, desplazar_x):
""" Para cuando el usuario se desplaza a la izquierda/derecha y necesitamos mover
todo: """
# Llevamos la cuenta de la cantidad de desplamiento
self.desplazar_escenario += desplazar_x
# Iteramos a través de todas las listas de sprites y desplazamos
for plataforma in self.listade_plataformas:
plataforma.rect.x += desplazar_x
for enemigo in self.listade_enemigos:
enemigo.rect.x += desplazar_x
# Creamos las plataformas para el nivel
class Nivel_01(Nivel):
""" Definición para el nivel 1. """
def __init__(self, protagonista):
""" Creamos el nivel 1. """
# Llamamos al constructor padre
Nivel.__init__(self, protagonista)
self.limitedel_nivel = -1000
# Array con el largo, alto, x, e y de la plataforma
nivel = [ [210, 70, 500, 500],
[210, 70, 800, 400],
[210, 70, 1000, 500],
[210, 70, 1120, 280],
]
# Iteramos a través del array anterior y añadimos plataformas
for plataforma in nivel:
bloque = Plataforma(plataforma[0], plataforma[1])
bloque.rect.x = plataforma[2]
bloque.rect.y = plataforma[3]
bloque.protagonista = self.protagonista
self.listade_plataformas.add(bloque)
# Creamos las plataformas para el nivel
class Nivel_02(Nivel):
""" Definición para el nivel 2. """
def __init__(self, protagonista):
""" Creamos el nivel 1. """
# Llamamos al constructor padre
Nivel.__init__(self, protagonista)
self.limitedel_nivel = -1000
# Array con el largo, alto, x, e y de la plataforma
nivel = [ [210, 30, 450, 570],
[210, 30, 850, 420],
[210, 30, 1000, 520],
[210, 30, 1120, 280],
]
# Iteramos a través del array anterior y añadimos plataformas
for plataforma in nivel:
bloque = Plataforma(plataforma[0], plataforma[1])
bloque.rect.x = plataforma[2]
bloque.rect.y = plataforma[3]
bloque.protagonista = self.protagonista
self.listade_plataformas.add(bloque)
def main():
""" Programa Principal """
pygame.init()
# Establecemos el alto y largo de la pantalla
dimensiones = [LARGO_PANTALLA, ALTO_PANTALLA]
pantalla = pygame.display.set_mode(dimensiones)
pygame.display.set_caption("Side-scrolling Plataformer")
# Creamos al protagonista
protagonista = Protagonista()
# Creamos todos los niveles
listade_niveles = []
listade_niveles.append(Nivel_01(protagonista))
listade_niveles.append(Nivel_02(protagonista))
# Establecemos el nivel actual
nivel_actual_no = 0
nivel_actual = listade_niveles[nivel_actual_no]
listade_sprites_activas = pygame.sprite.Group()
protagonista.nivel = nivel_actual
protagonista.rect.x = 340
protagonista.rect.y = ALTO_PANTALLA - protagonista.rect.height
listade_sprites_activas.add(protagonista)
#Iteramos hasta que el usuario hace click sobre el botón de salir.
hecho = False
# Usado para gestionar cuán rápido se actualiza la pantalla.
reloj = pygame.time.Clock()
# -------- Bucle Principal del Programa -----------
while not hecho:
for evento in pygame.event.get(): # El usuario realizó alguna acción
if evento.type == pygame.QUIT: #Si el usuario hizo click en salir
hecho = True # Marcamos como hecho y salimos de este bucle
if evento.type == pygame.KEYDOWN:
if evento.key == pygame.K_LEFT:
protagonista.ir_izquierda()
if evento.key == pygame.K_RIGHT:
protagonista.ir_derecha()
if evento.key == pygame.K_UP:
protagonista.saltar()
if evento.type == pygame.KEYUP:
if evento.key == pygame.K_LEFT and protagonista.cambio_x < 0:
protagonista.stop()
if evento.key == pygame.K_RIGHT and protagonista.cambio_x > 0:
protagonista.stop()
# Actualizamos al protagonista.
listade_sprites_activas.update()
# Actualizamos los objetos en el nivel
nivel_actual.update()
# Si el protagonista se aproxima al borde derecho, desplazamos el escenario a la izquierda(-x)
if protagonista.rect.x >= 500:
diff = protagonista.rect.x - 500
protagonista.rect.x = 500
nivel_actual.escenario_desplazar(-diff)
# Si el protagonista se aproxima al borde izquierdo, desplazamos el escenario a la derecha(+x)
if protagonista.rect.x <= 120:
diff = 120 - protagonista.rect.x
protagonista.rect.x = 120
nivel_actual.escenario_desplazar(diff)
# Si el protagonista alcanza el final del nivel, pasa al siguiente
posicion_actual = protagonista.rect.x + nivel_actual.desplazar_escenario
if posicion_actual < nivel_actual.limitedel_nivel:
protagonista.rect.x = 120
if nivel_actual_no < len(listade_niveles)-1:
nivel_actual_no += 1
nivel_actual = listade_niveles[nivel_actual_no]
protagonista.nivel = nivel_actual
# TODO EL CÓDIGO DE DIBUJO DEBERÍA IR DEBAJO DE ESTE COMENTARIO
nivel_actual.draw(pantalla)
listade_sprites_activas.draw(pantalla)
# TODO EL CÓDIGO DE DIBUJO DEBERÍA IR ENCIMA DE ESTE COMENTARIO
# Limitamos a 60 fps
reloj.tick(60)
# Avanzamos y actualizamos la pantalla que ya hemos dibujado
pygame.display.flip()
# Pórtate bien con el IDLE. Si olvidas esta línea, el programa se 'colgará'
# al salir.
pygame.quit()
if __name__ == "__main__":
main()