Curso Python. Volumen XXI: Pygame, videojuegos en python. Parte VII
Bienvenidos un día más al curso de Python. En el capítulo anterior ya os mostramos cómo se añadían los controles de teclado para que el usuario pueda interactuar con nuestra aplicación. En este capítulo, vamos a mejorar los controles de usuario, de modo que el usuario tenga una mejor experiencia cuando utilice el videojuego que estamos realizando con la popular librería, ademas de agregar un control de colisiones. Así que pongámonos manos a la obra.
Mejorando los controles de usuario en Pygame
Si os dísteis cuenta con el código del capítulo anterior, la paleta sólo se movía una vez, aunque mantuviéramos la tecla pulsada, en vez de mantenerse moviendo la paleta hasta que se suelte la tecla, y además, no agregamos que la paleta pudiera ser controlada por el ratón.
Para lo primero, hay que indicarle a nuestra aplicación que active la repetición de teclas, lo cual se consigue utilizando la función “pygame.key.set_repeat()”, esta función requiere de dos argumentos: el primero que se encarga de establecer el tiempo de retraso, es decir, los milisegundos, que deben transcurrir para que detecte o envíe el primer evento. El segundo argumento, es el intervalo en milisegundos que debe transcurrir entre cada envío del evento.
Para conocer el estado del ratón, hay que usar “pygame.mouse”, para ello, lo primero que haremos es que el cursor del ratón no se vea dentro de la pantalla, es decir, que sólo aparezca fuera de ella, esto lo conseguimos del siguiente modo: “pygame.mouse.set_visible(False)”.
Después, dentro de nuestro bucle, tendremos que registrar la posición del ratón con “pos_mouse = pygame.mouse.get_pos()”, que nos devuelve una tupla con las coordenadas (x, y) en donde se encuentra el puntero del ratón, que quedarán almacenadas en “pos_mouse”. Luego usaremos “mov_mouse = pygame.mouse.get_rel()” para obtener cuánto se ha movido el ratón desde la última consulta que realizó “get_rel()”, esta también nos devuelve una tupla, del tipo (x,y) indicándonos la distancia que se ha movido, en caso de que el puntero del ratón no se haya movido devolverá un (0,0).
elif mov_mouse[1] != 0:
jugador1.rect.centery = pos_mouse[1]
En la parte del código donde comprobamos los eventos, usamos un “if mov_mouse[1] != 0:”, con esto le decimos que si la coordenada “y” de la tupla que devuelve “get_rel()” es distinta de “0”, mueva la paleta a la posición en donde está el ratón, “jugador1.rect.centery = pos_mouse[1]”. Entonces nuestro código quedaría del siguiente modo:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ---------------------------
# Importacion de los módulos
# ---------------------------
import pygame
from pygame.locals import *
import os
import sys
# -----------
# Constantes
# -----------
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
IMG_DIR = "imagenes"
# ------------------------------
# Clases y Funciones utilizadas
# ------------------------------
def load_image(nombre, dir_imagen, alpha=False):
# Encontramos la ruta completa de la imagen
ruta = os.path.join(dir_imagen, nombre)
try:
image = pygame.image.load(ruta)
except:
print("Error, no se puede cargar la imagen: " + ruta)
sys.exit(1)
# Comprobar si la imagen tiene "canal alpha" (como los png)
if alpha is True:
image = image.convert_alpha()
else:
image = image.convert()
return image
# -----------------------------------------------
# Creamos los sprites (clases) de los objetos del juego:
class Pelota(pygame.sprite.Sprite):
#"La bola y su comportamiento en la pantalla"
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = load_image("bola.png", IMG_DIR, alpha=True)
self.rect = self.image.get_rect()
self.rect.centerx = SCREEN_WIDTH / 2
self.rect.centery = SCREEN_HEIGHT / 2
self.speed = [3, 3]
def update(self):
if self.rect.left < 0 or self.rect.right > SCREEN_WIDTH:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > SCREEN_HEIGHT:
self.speed[1] = -self.speed[1]
self.rect.move_ip((self.speed[0], self.speed[1]))
class Paleta(pygame.sprite.Sprite):
#"Define el comportamiento de las paletas de ambos jugadores"
def __init__(self, x):
pygame.sprite.Sprite.__init__(self)
self.image = load_image("paleta.png", IMG_DIR, alpha=True)
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = SCREEN_HEIGHT / 2
def humano(self):
# Controlar que la paleta no salga de la pantalla
if self.rect.bottom >= SCREEN_HEIGHT:
self.rect.bottom = SCREEN_HEIGHT
elif self.rect.top <= 0:
self.rect.top = 0
# ------------------------------
# Funcion principal del juego
# ------------------------------
def main():
pygame.init()
# creamos la ventana y le indicamos un titulo:
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Pong ")
# cargamos los objetos
fondo = load_image("fondo.jpg", IMG_DIR, alpha=False)
bola = Pelota()
jugador1 = Paleta(40)
clock = pygame.time.Clock()
pygame.key.set_repeat(1, 25) # Activa repeticion de teclas
pygame.mouse.set_visible(False)
# el bucle principal del juego
while True:
clock.tick(60)
# Obtenemos la posicion del mouse
pos_mouse = pygame.mouse.get_pos()
mov_mouse = pygame.mouse.get_rel()
# Actualizamos los objetos en pantalla
jugador1.humano()
bola.update()
# Posibles entradas del teclado y mouse
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit(0)
elif event.type == pygame.KEYDOWN:
if event.key == K_UP:
jugador1.rect.centery -= 5
elif event.key == K_DOWN:
jugador1.rect.centery += 5
elif event.key == K_ESCAPE:
sys.exit(0)
elif event.type == pygame.KEYUP:
if event.key == K_UP:
jugador1.rect.centery += 0
elif event.key == K_DOWN:
jugador1.rect.centery += 0
# Si el raton no esta quieto mover la paleta a su posicion
elif mov_mouse[1] != 0:
jugador1.rect.centery = pos_mouse[1]
# actualizamos la pantalla
screen.blit(fondo, (0, 0))
screen.blit(bola.image, bola.rect)
screen.blit(jugador1.image, jugador1.rect)
pygame.display.flip()
if __name__ == "__main__":
main()
Colisiones
Hasta el momento la pelota cada vez que se encuentra con la paleta la atraviesa, esto es debido a que todavía no hemos indicado a la aplicación qué ocurre cuando se producen colisiones, es decir, cuando dos “sprites” chocan.
Para esto, dentro de la clase de uno de los “sprites” tendríamos que crear un método que compruebe si este “sprite” ha colisionado con otros, por conveniencia lo vamos a colocar en la clase “Pelota”, debido a que ésta es la que rebota con todos los elementos.
Entonces, en la clase “Pelota” definimos la función “def colision(self, objetivo):” la cual se encargará de comprobar si la pelota ha chocado con algo, el segundo argumento es el objeto con el cual esperamos que choque.
Para saber si dos “sprites/objetos” han chocado, usamos la función “pygame.sprite.colliderect(objeto1, objeto2)” que nos proporciona la propia librería, esta función comprueba si el rectángulo del “objeto1” entra en contacto con el rectángulo del “objeto2”, devolviendo “True” en caso de que entren en contacto. Si entran en contacto, lo único que tendremos que hacer es cambiar la dirección de la pelota. A continuación, os mostramos cómo:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ---------------------------
# Importacion de los módulos
# ---------------------------
import pygame
from pygame.locals import *
import os
import sys
# -----------
# Constantes
# -----------
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
IMG_DIR = "imagenes"
# ------------------------------
# Clases y Funciones utilizadas
# ------------------------------
def load_image(nombre, dir_imagen, alpha=False):
# Encontramos la ruta completa de la imagen
ruta = os.path.join(dir_imagen, nombre)
try:
image = pygame.image.load(ruta)
except:
print("Error, no se puede cargar la imagen: " + ruta)
sys.exit(1)
# Comprobar si la imagen tiene "canal alpha" (como los png)
if alpha is True:
image = image.convert_alpha()
else:
image = image.convert()
return image
# -----------------------------------------------
# Creamos los sprites (clases) de los objetos del juego:
class Pelota(pygame.sprite.Sprite):
#"La bola y su comportamiento en la pantalla"
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = load_image("bola.png", IMG_DIR, alpha=True)
self.rect = self.image.get_rect()
self.rect.centerx = SCREEN_WIDTH / 2
self.rect.centery = SCREEN_HEIGHT / 2
self.speed = [3, 3]
def update(self):
if self.rect.left < 0 or self.rect.right > SCREEN_WIDTH:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > SCREEN_HEIGHT:
self.speed[1] = -self.speed[1]
self.rect.move_ip((self.speed[0], self.speed[1]))
def colision(self, objetivo):
if self.rect.colliderect(objetivo.rect):
self.speed[0] = -self.speed[0]
class Paleta(pygame.sprite.Sprite):
#"Define el comportamiento de las paletas de ambos jugadores"
def __init__(self, x):
pygame.sprite.Sprite.__init__(self)
self.image = load_image("paleta.png", IMG_DIR, alpha=True)
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = SCREEN_HEIGHT / 2
def humano(self):
# Controlar que la paleta no salga de la pantalla
if self.rect.bottom >= SCREEN_HEIGHT:
self.rect.bottom = SCREEN_HEIGHT
elif self.rect.top <= 0:
self.rect.top = 0
# ------------------------------
# Funcion principal del juego
# ------------------------------
def main():
pygame.init()
# creamos la ventana y le indicamos un titulo:
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Pong")
# cargamos los objetos
fondo = load_image("fondo.jpg", IMG_DIR, alpha=False)
bola = Pelota()
jugador1 = Paleta(40)
clock = pygame.time.Clock()
pygame.key.set_repeat(1, 25) # Activa repeticion de teclas
pygame.mouse.set_visible(False)
# el bucle principal del juego
while True:
clock.tick(60)
# Obtenemos la posicon del mouse
pos_mouse = pygame.mouse.get_pos()
mov_mouse = pygame.mouse.get_rel()
# Actualizamos los objetos en pantalla
jugador1.humano()
bola.update()
# Comprobamos si colisionan los objetos
bola.colision(jugador1)
# Posibles entradas del teclado y mouse
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit(0)
elif event.type == pygame.KEYDOWN:
if event.key == K_UP:
jugador1.rect.centery -= 5
elif event.key == K_DOWN:
jugador1.rect.centery += 5
elif event.key == K_ESCAPE:
sys.exit(0)
elif event.type == pygame.KEYUP:
if event.key == K_UP:
jugador1.rect.centery += 0
elif event.key == K_DOWN:
jugador1.rect.centery += 0
# Si el raton no esta quieto mover la paleta a su posicion
elif mov_mouse[1] != 0:
jugador1.rect.centery = pos_mouse[1]
# actualizamos la pantalla
screen.blit(fondo, (0, 0))
screen.blit(bola.image, bola.rect)
screen.blit(jugador1.image, jugador1.rect)
pygame.display.flip()
if __name__ == "__main__":
main()
Aquí lo dejamos por hoy, os recomendamos que hagáis un repaso de lo aprendido por el momento y que experimentéis.
En el próximo capítulo haremos que nuestro oponente se mueva de manera autónoma y también añadiremos audio a nuestro videojuego. Comentaros como siempre, que todos los que se acaban de incorporar al curso tienen un índice con todos los capítulos del curso, ya que nunca es tarde para empezar.
Via: www.redeszone.net