Google+ Seguidores

viernes, 22 de mayo de 2015

Juego en Pygame - Piedra Spock Papel Lagarto y Tijera

    3

Piedra-Spock-Papel-Lagarto-Tijera
Piedra-Spock-Papel-Lagarto-Tijera
Este juego en PyGame es una variante del clásico juego "piedra, papel o tijera" que ya se vio hace tiempo en el blog: Piedra, papel o tijera en python, con la diferencia que se agregan dos manos: Spock y Lagarto.

Puedes ver la explicación del juego en este enlace:  http://es.wikihow.com/jugar-piedra,-papel,-tijera,-lagarto,-spock

Aquí te dejo la famosa explicación que dio Sheldon Cooper de The Bing Bang Theory:




Pues en esta entrada recrearemos este juego con todo y su interfaz gráfica, terminaremos con nuestro propio y funcional juego de "piedra-spock-papel-lagarto-tijeras" puffff, un nombre bastante largo para un juego ¿no creen?, así que por motivos prácticos lo llamaremos PSPTL.. (a veces me asombro por mi originalidad xD).
El juego se verá así:


Vista del juego en PyGame
Vista del juego en PyGame

Función del juego


En la parte inferior se muestran los botones para seleccionar una mano y una vez seleccionada aparecerán en el medio de la pantalla la mano que seleccionamos contra la que seleccionó la computadora mostrando los resultados: ganas, pierdes o empate y actualizando las correspondientes puntuaciones.
Antes de empezar quiero aclarar que es recomendable un nivel medio de Python para entender todos los conceptos que manejare más adelante. Puedes pasarte por el Tutorial Python si recién están comenzando. Pero aún si no entiendes muchas cosas te animo a seguir, tu escribes el código tal y como te lo muestro aunque no lo entiendas del todo, todo esto es parte de aprender a programar, poco a poco tu mente se irá despejando y si continuas aprendiendo, te acordaras de aquel día que viste este tutorial con el que hiciste el juego PSPLT que en su momento no entendías mucho, pero ahora si (créeme, así me ha pasado muchas veces).
Por último yo utilizo Sublime Text 2 como editor de texto y ejecuto el programa de 2 maneras:
1 - Ejecutando el archivo .py por medio del símbolo del sistema (CMD).
2 - En Sublime Text si presiona las teclas "Ctrl + B", el código se ejecutará.

Por último es necesario tener instalado la versión de Python 2.7.9 y PyGame 1.9.1 (para python 2.7)
Quye puedes descargar aquí:
Python 2.7.9 --> https://www.python.org/ftp/python/2.7.9/python-2.7.9.msi
PyGame 1.91 --> http://pygame.org/ftp/pygame-1.9.1.win32-py2.7.msi

Sin más, empezamos:

Primeramente descarga este archivo y lo descomprimes en una carpeta: https://www.dropbox.com/s/k9fdpu3cm45m4ic/PSPTL.rar?dl=1

Dentro vienen los recursos que necesitaremos para crear nuestro juego (plantilla e imágenes png).

Abrimos la plantilla en el editor de texto de nuestra preferencia, esta la podemos utilizar para cualquier proyecto PyGame que tengan en mente y que quieran realizar, esta se divide en:

MÓDULOS: aquí importamos todos los módulos que vallamos a ocupar en nuestro programa.

CONSTANTES: aquí declaramos las constantes, es decir, valores que no cambian nunca y se escriben en mayúsculas.

FUNCIONES DE AYUDA: aquí creamos las funciones que utilizas constantemente a lo largo de todo tu código y no son exclusivas de alguna clase.

CLASES: aquí van las clases de tu código (duuhh!!).

FUNCIÓN PRINCIPAL: esta es la encargada de que la magia ocurra... =D.

Si ejecutas el código se abrirá una ventana con las dimensiones y etiquetas especificadas en la función main(), así:


Ventana del juego en PyGame
Ventana del juego en PyGame

Un breve análisis de la plantilla


Módulos
Módulos

La primera línea #! Enconding: utf-8 es importante si queremos escribir el famoso acento (áéíóú) entre otros caracteres especiales. Importamos los módulos Pygame, random y sys que ucuparemos en nuestro programa.






Constantes
Constantes

He creado estas constantes que utilizaremos más adelante.








Funciones de ayuda
Funciones de ayuda
Esta es una función que nos facilitara el dibujado de caracteres en la ventana: más información sobre el módulo pygame.font.Font aquí: http://www.pygame.org/docs/ref/font.html#pygame.font.Font



Clases en python
Clases
 

Aquí crearemos las clases necesarias.









main() en python
main()
pygame.init() inicia PyGame.
pantalla = pygame.display.set_mode((ancho, alto)) crea la ventana que vieron si ejecutaron el código.
pygame.display.set_caption("Mi juego") Asigna la etiqueta que le escribamos a la ventana. reloj = pygame.time.Clock() iniciamos el reloj. marcha = True variable con la que controlaremos el bucle principal.

Aquí iniciamos el bucle infinito hasta que en algún momento del código cambiemos la variable marcha a False (que es cuando queremos salir de la aplicación). 

reloj.ticks(FPS) determina cuantas veces por segundo se ejecuta el código, es decir, el bucle while que hemos establecido en 30 en las constantes.

Enseguida están los manejadores de eventos que se encargan de detectar todas nuestras acciones, en este caso esta solamente la que detecta cuando damos click en la [X] de la ventana para terminar nuestro bucle (esto se explicará más adelante con más detalle).

pantalla.fill(NEGRO) borra toda la pantalla con un fondo negro antes de dibujar nuestro juego.
pygame.display.flip() como dice en la plantilla actualiza y avanza

Funcion principal Python
Principal

main() ejecuta nuestra función principal. pygame.quit() y sys.exit() cierra correctamente los módulos cuando salgamos de la función main().



Bueno, ya estamos a un paso menos de crear nuestro emocionante juego PSPLT.

Continuando...

CLASES:
Como mencionamos anteriormente, este juego consta de 5 opciones que podríamos catalogar como "objetos" y ¿Qué tienen en común estos objetos? Pues que son "Manos".

Lo primero que vamos a hacer es crear por así decirlo el molde de estas 5 manos, por ejemplo:
Tienes un molde con el que vas a fabricar 5 vasijas, todas van a ser idénticas pero lo que las va a diferenciar son las características que le agregues a cada una después de salir del molde, como puede ser el color, algún grafiti, etc. (espero no haberte confundido más con ese ejemplo xD). Pues eso mismo haremos pero con las manos. Puedes pasarte por el artículo: Clases y objetos en python

Así que vamos a crear una clase a la que le especificaremos que características debe de tener cada una de las manos al momento de crearlas. Esta clase va a controlar que imagen va a cargar para cada mano, le asignara su nombre, su posición, entre otras características que veremos más adelante y definiremos sus métodos, como por ejemplo, un método que controle como debe actuar este botón cuando lo presionemos.

En la sección de clases escribiremos este código:

Clase Manos
Clase Manos
Análisis:

Método __init__
Método __init__
Línea 31 - Creamos nuestra clase Manos y le pasamos la clase padre 
pygame.sprite.Sprite *por el momento no es necesario que entiendan sobre clases padres y subclases, esta es la sintaxis que pide la documentación de PyGame para que funcione, más información aquí: http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Sprite
Linea 32 - Método inicializador de nuestra clase a la cual le pasaremos los parámetros texto, x e y cuando creemos los objetos (manos).
Linea 33 - llama al método inicializador de pygame.sprite.Sprite
Linea 34 - Al nosotros utilizar el módulo pygame.sprite.Sprite, es porque pretendemos utilizar alguna imagen por lo que es obligatorio crear la variable self.image (no debe llamarse de otra forma) en la que se cargara la imagen correspondiente con pygame.image.load() dependiendo de lo que diga el parámetro "texto" (lo entenderás mejor más adelante)
Linea 35 - Con self.image.get_rect() obtenemos los valores de posición y dimensiones de la imágen que cargamos y los almacenamos en la variable self.rect. 

Linea 36 - self.rect.topleft asigna la posición que va a tener en la pantalla la imagen con los valores x e y.
Linea 37 a 41 - son variables adicionales que he creado para usar más adelante; self.copia_imagen guardo una copia de la imagen, self.tipo guardo el nombre de la imagen, self.x y self.y almaceno las coordenadas y self.factor_reduccion.
 
Esta clase tendrá 4 métodos:

Métodos de la clase Manos
Métodos de la clase Manos
El método obtener_imagen() me regresa una copia de la imagen
El método obtener_rect() me regresa una copia de la variable self.rect
El método tipo_mano() me regresa el nombre (en formato string) de la mano
El método presionar() lo que hace es que al presionar la imagen esta reduce su dimensión para dar esa sensación de que la estamos presionando y al soltar el click o quitar el cursor de la imagen esta vuelva a su tamaño normal. El módulo pygame.transform.scale(imagen, "escala deseada") lo que hace es tomar una imagen y cambiar su dimensión. *más información sobre este módulo aquí: http://www.pygame.org/docs/ref/transform.html#pygame.transform.scale

Bien, hemos construido el molde, pero ahora vamos a utilizarlo, así que crearemos una clase que se va a encargar de eso de la lógica del juego que llamaremos Juego()


Clase Juego()
Clase Juego()
La clase Juego() también va a contar con su método inicializador.

Linea 63 a 72 - Primero creamos una lista vacía self.manos donde guardaremos las 5 manos de nuestro juego, creo una variable posicion_inicial a la que le asigno la posición vertical que tendrán nuestras manos en la pantalla. 

Enseguida vamos a utilizar nuestra clase Manos para crear las 5 manos del juego; vamos a crear la mano "piedra" de la siguiente manera:
Manos(piedra", 10, posicion_vertical) regresando un poco cuando estábamos creando la clase Manos...¿recuerdan que a esta le pusimos que pidiera los parámetros texto, x e y? pues con esta línea estamos diciendo que se cree un objeto en donde sus características serán texto = "piedra", x = 10 e y = posicion_vertical.
Y con self.manos.append() le estamos diciendo que ese objeto lo agregue a la lista self.manos.

Al crear las 5 manos vamos a terminar con una lista que contiene 5 objetos; self.mano = [Objeto1, Objeto2, Objeto3, Objeto4, Objeto5] y cada uno de estos objetos con sus respectivas características.
Por último self.todos_los_sprites = pygame.sprite.Group(self.manos) es una característica de pygame que nos permite controlar más fácilmente listas de objetos creados con el módulo pygame.sprite.Sprite (revisa la documentación de PyGame para más información) * Como dije anteriormente no necesitas entenderlo por el momento solo escríbelo.

Linea 74 a 79 - las primeras 3 variables son para almacenar que escogió el jugador, la computadora y el resultado, las siguientes 3 preparamos el texto que se va mostrar en la pantalla apoyándonos de la función de ayuda texto().

Linea 81 a 89 - en self.vs_image guardo la imagen de vs que aparecerá al centro, self.mano_seleccionada guardara la mano que hallamos seleccionado al soltar el click sobre una de las manos, self.jugador_imagen y self.comp_imagen guardaremos una copia de las imágenes que seleccionaron el jugador y la computadora por juego y por último self.puntuacion que ya se imaginaran para que es.

Enseguida nuestra clase Juego() contara con 9 métodos:
Los primeros 5:

5 métodos de la clase Juego()
5 métodos de la clase Juego()
El método copiar_imagen() nos regresara una copia de la imagen amplificada con pygame.transform.scale que le corresponda al nombre que le demos, por ejemplo imagen_piedra = copiar_imagen("piedra") con esto guardamos una copia de la imagen de la mano piedra en la variable imagen_piedra.

El método obtener_manos() simplemente me regresa la lista self.manos

El método seleccionar() guarda en la variable self.mano_seleccionada la mano u objeto en la que hayamos dado un click.

El método obtener_mano_seleccionada() nos regresara que mano tenemos seleccionada en ese momento.

El método dibujar() que le pasamos el parámetro pantalla que es en donde vamos a dibujar nuestro juego.
Con pantalla.blit() dibujamos y colocamos todos los textos e imágenes que mostrará nuestro juego.
Noten que hay una condición if self.jugador_imagen esta variable la iniciamos como None por lo que la condición if la considera como Falso y no dibujara lo que esta contenga hasta que tengamos una imagen en la variable.
Al final con self.todos_los_sprites.draw(pantalla) dibujamos todas las manos.

Los siguientes 3 métodos vienen siendo el corazón del juego, la lógica que determina si ganas, pierdes o empatas:


3 métodos de la clase Juego()
3 métodos de la clase Juego()
El método nombre_a_numero() regresa un número dependiendo de la mano que introduzcamos. Ej. nombre_a_numero("papel") nos regresa 2

El método numero_a_nombre() hace lo contrario al método anterior, introducimos un número y nos regresa la mano correspondiente.

Y el método principal jugar() se le pasa la mano que escogió el jugador:
Guardamos el nombre de la mano en self.jugador_escoge, guardamos la imagen de la mano que escogió el jugador en self.jugador_imagen y guardamos el número que le corresponde a la mano del jugador en numero_jugador

Guardamos un número al azar del 0 al 4 en numero_comp, guardamos el nombre que le corresponde a ese número en self.comp_escoge y guardamos la imagen que le corresponde al nombre en self.comp_imagen.

Aquí viene lo interesante que es determinar el resultado. Pudimos haber hecho una larga lista de sentencias if y elif con todos los diferentes casos para obtenerlo pero buscando un poco en Internet encontré este pequeño código que nos evita todo eso:

res = (numero_jugador - nombre_comp) % 5
Y dependiendo del número que se almacene en res podemos determinar el resultado con las últimas 3 sentencias if y elif... Genial!!! y dependiendo de quién gana, asignamos sus respetiva puntuación.


Y por último:
Método actualizar()
Método actualizar()
El método actualizar():
*esta parte puede ser confusa así que tratare de explicar lo mejor posible
Con pygame.mouse.get_pressed()[0] detecta cuando tenemos presionado el click izquierdo, con pygame.mouse._get_pos() guardamos en la variable mouse la posición que tiene es ese momento el mouse, con self.seleccionar(None) llamamos al método de la clase en la que nos encontramos (Juego()) para resetear la variable self.mano_seleccionada (en caso de que tengamos otra mano almacenada).

Con for mano in self.obtener_manos() recorremos nuestras 5 manos y en la condición if llamamos al método obtener_rect() de la mano en la que nos encontramos y así mismo llamamos otro método de nuestra librería pygame .collidepoint(mouse), lo que hace es detectar si el mouse se encuentra encima de la imagen de la mano.

En resumen... sería:
Si (if) mantenemos el click izquierdo encima de alguna de las manos, utiliza el método mano.presionar(True) para decirle a esa mano que está siendo presionada y con self.presionar(mano) la guarda en mi variable self.mano_seleccionada.
De lo contrario (else) usa mano.presionar(False).
* Si lo se, yo también tarde en entender todo esto.

Muy bien... ya casi terminamos, solo nos falta hacer funcionar todo esto por medio de nuestra función principal main() como dije anteriormente, aquí es donde la magia ocurre xD, es decir, donde conectamos todos los eventos ayudándonos de los métodos que hemos creado para cada clase con el fin de que todo funcione correctamente.

Nuestra gloriosa función principal:
Función principal del Juego
Función principal del Juego

Aquí solo hay 2 diferencias con respecto a la plantilla:
1 - hemos cambiado el nombre de la etiqueta
2 - hemos iniciado nuestra clase Juego() con juego = Juego()

Así que solo agreguen eso y continuamos...


Continuamos con el código
Continuamos con el código
Linea 196 - Esta se encarga de iterar por todos los eventos que pudieran ocurrir y los if-elif se encargan de detectar si alguno se cumple y ejecutar el código que necesitemos.
Linea 197 - El evento pygame.QUIT que ya venia en la plantilla y explique anteriormente

Linea 200 - El evento pygame.KEYDOWN detecta cuando alguna tecla a sido presionada y con pygame.K_ESCAPE detecta si hemos presionado la tecla "Esc" (Escape)


Linea 203 - El evento pygame.MOUSEBUTTONUP detecta si algún botón del mouse ha sido levantado.
Con juego.obtener_mano_seleccionada verificamos si se selecciono alguna mano, de ser así, llamamos al método principal de nuestra clase Juego() con juego.Jugar() al que si bien recuerdan debemos de pasarle la mano en forma de String(nombre de la mano) que esta seleccionada y eso lo hacemos con juego.obtener_mano_seleccionada().tipo_mano()

Linea 207 - le digo a todas las manos que no están siendo seleccionadas, esto para evitar errores a la hora de estarlas seleccionando (cuando terminemos ejecuta el juego quitando estas 2 líneas para que veas a lo que me refiero).

Excelente, ya estamos a 2 líneas de terminar... entiendo que todos estos conceptos son muy difíciles de asimilar en un principio, pero la única manera de terminar entendiendo todo esto es seguir practicando, no te desilusiones por no entenderlo, de momento yo también me sentí muchas veces así y quería dejarlo todo, pero seguí practicando y ahora lo entiendo todo, después de todo este es mi tutorial =)

Bien, solo nos queda agregar las líneas 213 y 218; juego.actualizar() y juego.dibujar(pantalla)

No se te ocurra poner la segunda línea arriba de pantalla.fill(NEGRO), si haces esto lo único que veras es el fondo negro ya que primero estarías dibujando el juego y después borrar la pantalla. Todo debe de tener su orden!

LISTO!!! Si hiciste todo bien y ejecutas el código, tu juego deberá funcionar como debe.

Les dejo el código final para su descarga aquí en caso de que hayan tenido algún problema y quieran comparar:
https://www.dropbox.com/s/3oqvap7qi9o4u7l/PSPTL%20final.py?dl=1


Demo del Juego...





Espero que esto le sirva de algo a alguien. Saludos


Salvador Ramirez

Autor: Salvador Ramirez

Seguir en Google +

3 comentarios:
Write comentarios
  1. hola! hice todo tal cual el tutorial lo dice me aparece el siguiente error: File "C:\Archivos de programa\Sublime Text 2\PSPLT.py", line 58
    self.image = self.copia_imagen
    ^
    IndentationError: expected an indented block
    [Finished in 0.5s with exit code 1]

    Podrias darme una mano para solucionarlo?
    muchas gracias y saludos. Santiago

    ResponderEliminar

Tu comentario es importante y nos motiva a seguir escribiendo...

Entradas más recientes

© 2014 Mi diario Python. Designed by Bloggertheme9 | Distributed By Gooyaabi Templates
Powered by Blogger.