Curso Python. Volumen XXI: Pygame, videojuegos en python. Parte X

Curso Python. Volumen XXI: Pygame, videojuegos en python. Parte X

Javier Ceballos Fernández

Bienvenidos un día más al curso de Python. En el capítulo anterior os enseñamos como podíais introducir efectos sonoros en el videojuego que estamos realizando con la librería Pygame. En este capítulo vamos a mejorar el juego tanto en el rendimiento como en la jugabilidad. Así que pongámonos manos a la obra.

Mejorando el juego

Según hemos ido evolucionando el juego, han aparecido unos puntos que se pueden mejorar, que son los siguientes:

  • Cuando se actualiza la pantalla, tenemos que cargar uno por uno los “sprites”, y esto puede resultar algo ineficiente.
  • El oponente es invencible.
  • Al marcar un punto, la bola debería volver al centro.

El primer punto es bastante fácil de corregir, lo único que tenemos que hacer es cargarlos todos a la vez de la siguiente forma “todos = pygame.sprite.RenderPlain(bola, jugador1, jugador2)” y una vez cargados los mostramos todos de una vez haciendo “todos.draw(screen)”.

Para el segundo punto, tendremos que realizar los siguientes:

def cpu(self, pelota):
self.speed = [0, 2.5]
if pelota.speed[0] >= 0 and pelota.rect.centerx >= SCREEN_WIDTH / 2:
if self.rect.centery > pelota.rect.centery:
self.rect.centery -= self.speed[1]
if self.rect.centery < pelota.rect.centery:
self.rect.centery += self.speed[1]

No ha variado mucho de la versión anterior, pero en este caso, hemos definido una velocidad (que en el eje “y” es de 2.5, lo cual es menor al 3 de la pelota) y luego se comprueba que la pelota se mueva a la derecha (hacia la paleta) con “pelota.speed[0] >= 0” y que la pelota haya pasado la mitad de la pantalla “pelota.rect.centerx >= SCREEN_WIDTH / 2”, si ambas condiciones se cumplen se comienza a mover la paleta, en caso contrario, se queda quieta.

En el caso que la pelota se mueva hacia la paleta, se compara la posición de la paleta con la pelota: si la pelota está más arriba que la paleta, ésta última se mueve hacia arriba, por otro lado, si la pelota está más abajo que la paleta, ésta se mueve hacia abajo.

Con esto el oponente ya no es invencible, debido a que:

  • Sólo se mueve si la pelota se acerca a él y ha pasado la mitad de la pantalla, por lo que el resto del tiempo está quieto (y por lo tanto la posición de la paleta y la pelota no coinciden siempre)
  • La paleta se mueve más lento que la pelota, por lo cual en tramos largos no es capaz de alcanzar a la pelota.
  • Combinando ambas, el ordenador puede perder, aunque la superficie de la paleta es mayor a la pelota.

Para el último problema, simplemente en la parte en donde se actualiza la bola, después de reproducir el sonido, reiniciamos la posición de la bola al centro.

La versión final del juego queda de la siguiente manera:

#!/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"
SONIDO_DIR = "sonidos"

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

def load_sound(nombre, dir_sonido):
ruta = os.path.join(dir_sonido, nombre)
# Intentar cargar el sonido
try:
sonido = pygame.mixer.Sound(ruta)
except (pygame.error) as message:
print("No se pudo cargar el sonido:" + ruta)
sonido = None
return sonido

# -----------------------------------------------
# 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, sonido_golpe, sonido_punto):
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]
self.sonido_golpe = sonido_golpe
self.sonido_punto = sonido_punto

def update(self):
if self.rect.left < 0 or self.rect.right > SCREEN_WIDTH:
self.speed[0] = -self.speed[0]
self.sonido_punto.play() # Reproducir sonido de punto
self.rect.centerx = SCREEN_WIDTH / 2
self.rect.centery = SCREEN_HEIGHT / 2
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]
self.sonido_golpe.play() # Reproducir sonido de rebote

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

def cpu(self, pelota):
self.speed = [0, 2.5]
if pelota.speed[0] >= 0 and pelota.rect.centerx >= SCREEN_WIDTH / 2:
if self.rect.centery > pelota.rect.centery:
self.rect.centery -= self.speed[1]
if self.rect.centery < pelota.rect.centery:
self.rect.centery += self.speed[1]

# ------------------------------
# Funcion principal del juego
# ------------------------------

def main():
pygame.init()
pygame.mixer.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)
sonido_golpe = load_sound("tennis.ogg", SONIDO_DIR)
sonido_punto = load_sound("aplausos.ogg", SONIDO_DIR)

bola = Pelota(sonido_golpe, sonido_punto)
jugador1 = Paleta(40)
jugador2 = Paleta(SCREEN_WIDTH - 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 raton
pos_mouse = pygame.mouse.get_pos()
mov_mouse = pygame.mouse.get_rel()

# Actualizamos los objetos en pantalla
jugador1.humano()
jugador2.cpu(bola)
bola.update()

# Comprobamos si colisionan los objetos
bola.colision(jugador1)
bola.colision(jugador2)

# Posibles entradas del teclado y raton
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))
todos = pygame.sprite.RenderPlain(bola, jugador1, jugador2)
todos.draw(screen)
pygame.display.flip()

if __name__ == "__main__":
main()

Aquí terminamos el curso, esperamos que os haya sido de utilidad. Os recomendamos que hagáis un repaso de todo lo aprendido y que rehagáis todos los ejemplos, ya que os ayudarán afianzar todos los conocimientos.

Comentaros como siempre, que el curso tiene un índice con todos los capítulos, el cual os podrá ayudar a dirigiros a los temas que queráis repasar en concreto.