Upload
ricardo-daniel-quiroga
View
265
Download
1
Embed Size (px)
DESCRIPTION
Capitulo 5 del curso
Citation preview
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
Programación de Juegos con PyGame
Capitulo 5: Fuentes, Temporizador y
Transformaciones
1
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
timeHasta ahora en la mayoria de los ejemplos hemos venido usando una clase llamada
Clock que pertenece al modulo pygame.time, para hacer que los juegos corran a una misma velocidad(en realidad todavia no se ninguno durante el curso) pero estava incluida en casi todos los ejemplos que presente.
En fin ese es el uso principal que tiene este modulo. Ya que como lo dice su nombre se llama time y permite gestionar controlar el tiempo osea permite especificar un retardo entre cada ciclo.
La duracion del retardo siempre se debe expecificar (o especifica) en milisegundos a diferencia del modulo estandar time que en este cuando se especifica un retardo se utiliza valores en coma flotante.
El retardo maximo que se puede aplicar o que la mayoria de las plataformas (SO) admiten es de 10 segundos osea expresado en el modulo pygame.time seria 10 * 1000.
get_ticks
Nos indica, el tiempo en milisegundo desde que se llamo a pygame.init().
Sintaxis:pygame.time.get_ticks() return int
wait
Pausa, Duerme el programa o script para permitir que se comparta CPU con otros procesos. Esta funcion es menos exacta que la funcion delay, la perdida de exactitud se debe a que cuando se llama a esta funcion se libera al cpu y la misma tarda (unas milesimas de segundo) en reobrar el control del procesador.
Sintaixis:pygame.time.wait(milisegundos) return int
delay
Pausa la ejecucion del programa durante un tiempo especificado en milisegundos, a diferencia de wait esta no duerme el programa por lo que estariamos hablando de espera activa. Como no todo es contra esta funcion tiene la caracteristica de ser mas exacta que wait().
Sintaxis:pygame.time.delay(milisegundos) return int
Nota: tanto wait() como delay() retornan la cantidad de milisegundos que duro el retardo o pausa.
2
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
set_timer
Nos permite definir un tipo de evento que se lanzara (aparecera en la cola de eventos) a intervalos de tiempo (como siempre especificados en milisegundos) regulares, si se quiere para el tenporizador para un evento que anteriormente definimo devemos pasarle el valor '0' al intervalo de tiempo.
El uso mas comun (si es que lo llegan a usar) de esta funcion es el de definir eventos del usuario, ya sean estos para levantar una bandera de control de algo o los que se les ocurra :P.
Clock
Este objecto es el que mas usaremos y estuvimos usando durante todo este curso. La principal funcion de este objecto es Proveer funciones que permitan gestionar o controlar la velocidad (framerate = Cantidad de cuadros por segundos.) del juego o mas explicitamente de nuestra aplicación en pygame.
Sintaxis:pygame.time.Clock()
tick
Permite limitar la velocidad ejecucion del juego, este metodo tendrias que llamarlo una ves por actualizacion.(ya que no es obligatorio que lo hagas (llamarlo una ves por actualizacion) aunque si quieres llamarlo en otro intervalo lo haces a conciencia (bueno lo peor que puede pasar sera o que el juego corra demaciado lento o demaciado rapido.))
Cuando hablo de actualizacion no me refiero explicitamente al bucle principal sino a cada actualizacion de pantalla osea deverias incluir esta funcion o su variantes en cada ciclo donde coloque por ejemplo el metodo pygame.display.flip() or decir.
El valor (argumento del metodo) especificado por framerate actuara como condicionador de la velocidad maxima del juego. Osea por ejemplo si llamamos a Clock.tick(50) esto no implica que el juego se ejecutara a 50 cuadros por segundo, sino que le dice a pygame que la velocidad maxima no superara los 50 cuadros por segundo, la velocidad puede ser menor y esto se puede dever a diferentes factores tales como la velocidad de la CPU donde se esta ejecutando el programa o la complegidad de los calculos que se realizan.
Por ultimo este metodo devuelve un entero que representa la cantdad de milisegundos, que transcurrieron entre las dos ultimas llamadas sucesivas al metodo tick().
Sintaxis:Clock.tick(framerate=0) return int
tick_busy_loop
3
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
Este metodo provee las mismas funcionalidades que tick() solo el mismo utiliza la CPU para mejorar la precicion de los calculos, osea es mas preciso que tick() pero en contrapartida tiene un costo de CPU mas elevado (Aunque con los CPU de hoy ni se debe notar, pero tampoco es bueno abusar de los recursos (Capacidad de Procesamiento de Datos) de la PC en tareas tan simples).
Sintaxis:Clock.tick_busy_loop(framerate=0) return int
Nota: Para lograr un efecto de fluides (que no parescan entre cortadas o muy lentas) en nuestras animaciones y juego se recomienda usar una velocidad promedio de entre 20-60 cuadros por segundos.
get_time
Retorna la cantidad de milisegundos que trascurrieron entre las 2 ultimas llamadas a la funcion tick() o tick_busy_loop().
Sintaxis:Clock.get_time(): return int
get_rawtime
Similar a get_time() solo que a diferencia de get_time esta omite los segundos que tick() estubo esperando para limitar la velocidad del juego.
Sintaxis:Clock.get_rawtime(): return int
get_fps
Esta funcion nos permite calcular el rendimiento o velocidad del juego (Cantidad de cuadros por segundo).
Sintaxis:Clock.get_fps() return float
En general el uso mas comun que tendra este modulo especificamente la clase pygame.time.Clock() sera limitar la velocidad de nuestro juego, para terminar este modulo crearemos una sencilla aplicación que mueve una imagen, aunque nos importara mostrar la velocidad actual (cantidad de cuadros por segundo y tiempo que transcurre entre actualizacion).
import pygame from pygame.locals import *
RESOLUCION = (400,400)
BLANCO = (255, 255, 255)
4
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
NEGRO = (0, 0, 0)
def main(): pygame.init() #inicializamos pygame pygame.font.init() #inicializamos el modulo para cargar fuentes screen = pygame.display.set_mode(RESOLUCION)
reloj = pygame.time.Clock() #nuestra muy usada funcion temporizador :P #cargamos la imagen imagen = pygame.image.load('imagenes/python.png').convert_alpha() #creo un objecto rect que contendra la pos de la imagen (ancho, alto ) rect = imagen.get_rect() #usaremos por defecto la fuente del Sistema mifont = pygame.font.Font(None, 25)
rect.x = - rect.w rect.y = (400 - rect.h) / 2 while 1: reloj.tick(50) screen.fill(BLANCO) #pinto el fondo de pantalla de blanco
rect.x += 3 #sencillo codigo que permite mover la imagen if rect.x >= 400: rect.x = - rect.w
#muestro la cantidad de cuadros por segundo text = "FPS: %s" %reloj.get_fps() print text, img = mifont.render(text, True, NEGRO) screen.blit(img, (10,10))
#muestro el retraso en segundos entre actulizacion text = "Retraso: %s" %(float(reloj.get_time()) / 1000) print text img = mifont.render(text, True, NEGRO) screen.blit(img, (10,50)) # dibujamos la imagen en pantalla screen.blit(imagen, rect) pygame.display.flip() for evento in pygame.event.get(): if evento.type == pygame.QUIT: return
if __name__ == '__main__': main()
5
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
(vease mov_img.py)
Lo unico estraño que encotraran en este codigo es el uso del modulo font (El mismo lo descibo mas abajo). No creo que tengan dificultad al entender el resto del codigo.
FontPyGame (en si SDL) a diferencia de las mayoria de las librerias graficas, nos provee
un conjunto de funcionalidades que nos permitira trabajar con fuentes TrueType y renderizarlas(Dibujarla) sobre una superficie.
La mayor parte del trabajo con fuentes se realiza usando los objetos Font. El módulo en sí solo tiene rutinas para inicializar el módulo y crear objetos Font con pygame.font.Font().
Opcionalmente se Puede cargar y trabajar con las fuentes desde el sistema (Fuentes instaladas por el Sistema Operativo) usando la función pygame.font.SysFont(). Hay otras funciones para ayudar a encontrar fuentes del sistema.
Pygame viene con una fuente incorporada por defecto. Utilza esta fuente cuando defines None el atributo de la misma.
Initcomo ya saben este metodo inicializa este modulo y ademas es llamada
automaticamente por pygame.init() aunque no es preferible confiarse ya que SDL_TTF
6
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
(librería que usa internamente el modulo Font) puede que no este incluida.
Sintaxis:pygame.font.init() return None
get_init, quit
Omito esplicar estos modulos por que considero que no tiene sentido, hace lo mismo que los otros modulos, osea preguntan si esta inicializado el modulo y desactiva el modulo respectivamente.
get_default_font
Permite obtener el la fuente por defecto del S.O. (solo el nombre y no la ruta completa, aunque esta esta en el path de pygame.) retornara el nombre de la fuente que pygame usara en caso de no pasar ningun parametro al atributo fuente de los metodos mas adelante.
Sintaxis:pygame.font.get_default_font(): return string
get_fontsRetornara un listado con todas las fuentes disponibles. Los nombres de la mismas
estaran en minuscula y en unicode. Aunque este metodo funciona en casi todos los sistemas operativos puede que en algunos retorne un array o lista vacio.
Sintaxis:pygame.font.get_fonts(): return list of strings
aquí un pequeño ejemplo de lo que obtendrian al llamar al metodo get_fonts():
7
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
>>> pygame.get_fonts()['kacstart', 'kacsttitle', 'dejavuserif', 'cmsy10', 'msam10', 'muktinarrow,\xc3\xa0\xc2\xa6\xc2\xae\xc3\xa0\xc2\xa7\xc2\x81\xc3\xa0\xc2\xa6\xe2\x80\xa2\xc3\xa0\xc2\xa7\xc2\x8d\xc3\xa0\xc2\xa6\xc2\xa4\xc3\xa0\xc2\xa6\xc2\xbf', 'freemono', 'cmr10', 'tahoma', 'dejavusans', 'freesans', 'umpush', 'kacstdigital', 'stencil', 'kacstbook', 'tlwgtypewriter', 'verdana', 'tlwgtypist', 'kacstpen', 'loma', 'phetsarathot', 'kacstoffice', 'mrykacstqurn',#omitido el resto de la salida.
match_fontNos permitira buscar una fuente especifica del sistema, si ademas colocamos en True
los argumentos bold e italic, intentara buscar la familia o fuente correcta. En caso de no encontrar nada retornara None.
Sintaxis:pygame.font.match_font(name, bold=False, italic=False): return path
>>> pygame.font.match_font('courier') /usr/lib/python2.6/dist-packages/pygame/sysfont.py:139: DeprecationWarning: os.popen3 is deprecated. Use the subprocess module. flin, flout, flerr = os.popen3('fc-list : file family style') '/home/wyrven/.fonts/cour.ttf'
Nota: Como ven este metodo lanza un DeprecationWarning que en criollo(lengua nativa o como se quiera uno explicar) significa Peligro Metodo Obsoleto, osea que las funciones que llama este metodo posiblemente desaparescan en proximas versiones de python. Eso solamente.
FontClase que utiliza pygame para poder trabajar y manipular las fuentes a partir de un
objecto o la ruta de un archivo. El constructor para crear un objecto Font requiere 2 argumentos el primero es la ruta de la fuente, si no se pasa ningun parametro usara por defecto la fuente del sistema; El segundo se refiere a la altura en pixels que tendra la misma.
Sintaxis:pygame.font.Font(filename, size): return Fontpygame.font.Font(object, size): return Font
Aunque los objectos fonts pueden emular efectos tales como Negrita y Cursiva es preferible cargar fuentes que posean esos atributos. Otra cosa importante es que despues de cargada la fuente el tamaño de esta ya no puede ser redefinida.
render
generara un nuevo objecto Surface con el texto dentro de ella, pygame no provee ningun metodo de dibujo directo por lo que tendra que generar la imagen y luego realizar el
8
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
volcado (Blit) sobre la superficie destino.
El texto a renderizarse solo puede contener una linea, no se aceptan caracteres no imprimibles tales como saltos de linea '\n', tabulacions '\t', etc.
sintaxis:Font.render(text, antialias, color, background=None): return Surface
El atributo text se refiere al texto que se desea dibujar o renderizar, antialias es un valor de tipo booleano y especifica si se desea o no suavizar los bordes, color: bueno el color del texto, background se refiere al color de fondo, si no se expecifica el color de fondo sera transparente.
La superficie retornada será del tamaño necesario para alojar el texto (si quieres conocer el tamaño que tendra un texto cualquiera consulta el metodo size()). Si se envía una cadena vacía en lugar de texto, se retornará una superficie negra que tendrá un pixel de ancho y la altura de la fuente.
Nota: el metodo render() retornará distintos tipos de superficie dependiendo del tipo de fondo y suavidad (antialias) solicitado. Por razones de rendimiento es bueno conocer el tipo de imagen que se usará. Si no usa antialias se retornará una superficie de 8 bits con una paleta de dos colores. Si el fondo es transparente se usará una transparencia por color (colorkey). Las imágenes con antialias se generan en superficies RGB de 24 bit, y se incluirá un pixel alpha si el fondo es transparente.
Como ya lo dijimos antes pygame no posee ningun metodo que permita dibujar texto sobre una superficie cualquiera, y para que no se les olvide les dejo los pasos que hay que seguir para lograr escribir texto sobre alguna superficie.
1)- Primero que nada cargar la fuente
#usare la fuente del sistema para este ejemplomi_fuente = pygame.Font(None, 20)
2)- se debe crear la imagen (superficie) que contendra el texto solicitado (osea renderizar)
texto = 'hola mundo, cruel...'img = mi_fuente.render(texto, False, NEGRO, None)
3)- y recien podemos dibujarla sobre la pantalla destino osea hacer un blit:
sup_destino.blit(img, (0, 0))
Y eso es todo ya logramos dibujar nuestro texto para terminar esta mini guia de como escribir texto vamos a un ejemplo practico donde ademas podras ver la diferencia entre usar antialias o y no usarlo.
9
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
(vease comp_antialias.py)
size
Permite calcular el tamaño que tendra la imagen con el texto renderizado antes de que se cree la superficie. Esto nos permite calcular la posicion que ocupara la imagen con el texto dibujado antes de que esta sea generada.
Sintaxis:Font.size(text): return (width, height)
bold, italic, underline
Estos metodos se refieren a definir efectos comunes que normalmente se le aplican a un texto que son:
bold: Negritaitalic: Cursivaunderline: subbrayado
Ademas pygame, nos permite mesclar estos efectos pero como anteriormente dijimos es conveniente que la fuente a la que le vamos a aplicar estos efectos, lo soporten, caso contrario pygame intentara emularlas aunque a veses no se obtendran buenos resultados con algunas fuentes.
10
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
En fin cada uno de estos efectos tiene dos metodos asociados, set_ y get_ el primero permite definir mediante un valor booleano si se aplicara o no dicho efecto, el segundo nos permite consultar dicho estado.
Sintaxis:Font.get_bold() return boolFont.set_bold(bool) return None
Font.get_italic() return boolFont.set_italic(bool) return None
Font.get_underline() return boolFont.set_underline(bool) return None
get_linesize
Retorna la altura en pixeles de una linea de texto. Se recomienda esta cantidad de espacio entre lineas cuando dibuje varias lineas de texto.
Sintaxis:Font.get_linesize(): return int
metrics
Retorna las medidas de cada letra de la cadena indicada. La lista retornada contiene tuplas para cada caracter, que contienen el desplazamiento mínimo en X, el desplazamiento máximo en X, el desplazamiento mínimo en Y, el desplazamiento mínimo en Y el desplazamiento anticipado de el otro caracter. [(minx, maxx, miny, maxy, advance), (minx, maxx, miny, maxy, advance), …].
sintaxis:Font.metrics(text): return list
Para mayor claridad muestro en la imagen donde podran entender con mayor claridad que significa y a que se refiere cada valor de cada tupla (minx, maxx, miny, maxy, advance) que representa una letra.
11
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
Otros metodos pertenecientes a la clase Font que no comentare ni explicare:
get_ascentget_descentget_heightget_width
SysFont
Retorna un nuevo objeto Font que se carga a partir de las fuentes del sistema. La fuente deberá coincidir con las opciones bold e italic solicitadas. Si no se encuentra una fuente de sistema adecuada esta función retornará la fuente por defecto de pygame. El parámetro name puede ser una lista de nombres para explorar.
Sintaxis:pygame.font.SysFont(name, size, bold=False, italic=False): return Font
transformPyGame nos proporciona un modulo adicional el cual nos permitira aplicar
transformanciones tales como escalado, rotacion, a nuestras imagenes (superficies), todos ellos toman como argunmento un superficie origen y devuelven un nuevo objecto surface al cual se le aplico la transformacion.
Alguna de las transformaciones se consideran destructivas. Esto significa que cada vez que se realizan se pueden perder pixeles de datos. Los ejemplos comunes de esto son las operaciones de escalado y rotación. Por ese motivo, es mejor transformar nuevamente la imagen original que seguir transformando un imagen muchas veces; por ejemplo, imagine que está animando un resorte que salta, que se expande y contrae con el movimiento. Si aplica los cambios de tamaño incrementalmente a las imágenes anteriores perderá detalle; en lugar de eso siempre comience con la imagen original y cambie el tamaño de la misma al tamaño deseado.
flip
Permite invertir una superficie tanto vertical como horizontalmente. xbool se refiere a si reflejara la imagen en vertical y ybool si se hara el reflejo de la imagen en horizontal.
Sintaxis:pygame.transform.flip(Surface, xbool, ybool): return Surface
Aca abajo dejo un ejemplo (captura de pantalla vea ustedes el codigo) del resultado que se obtiene aplicando flip()
12
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
(vease tranf_flip.py)
Para explicar como funciona, en la imagen de arriba el primer dibujo es el orginal, el de mas abajo es la imagen que obtubimos despues de aplicar la siguiente transformacion, en la cual solo reflejamos la imagen verticalmente osea (xbool = True, ybool = False):
>>> img = pygame.transform.flip(imagen, True, False)
scale
Redimensiona una superficie a un nuevo tamaño. Esta es una operación rápida que no suaviza los resultados. Se puede usar una superficie destino opcional, en lugar de tener que crear una nueva. Esto hace mas rápida la operación si tiene que cambiar el tamaño de algo muchas veces. De todas formas, la superficie destino debe tener el mismo tamaño que los parámetros width y height indicados. Además las superficie destino debe tener el mismo formato.
Sintaxis:pygame.transform.scale(Surface, (width, height), DestSurface=None):
return Surface
Ahora continuando con la misma imagen del ejemplo anterior aplicaremos el efecto scale con los siguientes argumentos:
img = pygame.transform.scale(imagen, (300, 200))
Y obtendremos el siguiente resultado:
13
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
rotate
Aplica una rotación inversa al sentido de las agujas del reloj sin suavizar. El argumento angle se representa en grados y puede ser cualquier número real. La magnitud negativa de angle rotará la imagen en sentido a las agujas del reloj.
A menos que las rotaciones se incrementen de a 90 grados, la imagen se rellenará para almacenarse en un nuevo tamaño. Si la imagen tiene pixels transparentes, el área de relleno será transparente. En otro caso pygame tomará un color que coincida con el valor clave de la imagen o el valor de la esquina superior izquierda.
Sintaxis:pygame.transform.rotate(Surface, angle): return Surface
Ejemplo:img = pygame.transform.rotate(imagen, 170)
14
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
(vease tranf_rotate.py)
rotozoom
Es una transformación combinada de rotación y cambio de tamaño. La superficie resultado será una superficie suavizada de 32 bits. El argumento scale es un número real que será multiplicado por la resolución actual. El argumento angle es un numero real que representa la rotación contra las agujas del reloj mediada en grados. Un ángulo de rotación negativo girará la superficie en sentido a las agujas del reloj.
sintaxis: pygame.transform.rotozoom(Surface, angle, scale): return Surface
ejemplo:img = pygame.transform.rotozoom(imagen, 75, 1.5)
(vease tranf_rotzoon.py)
scale2x
Duplica el tamaño de la imagen original, retorna una nueva imagen que será del doble de tamaño que la original. Utiliza el algoritmo AdvanceMAME Scale2X que produce escalado
15
antes despues
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
de gráficos evitando el dentado o los cuadrados grandes osea usando un antialias en la transformacion.
Sintaxis:pygame.transform.scale2x(Surface, DestSurface = None): Surface
smoothscale
Cambia el tamaño de una superficie con suavidad y de forma arbitraria. Usa uno de dos algoritmos diferentes para alterar cada una de las dimensiones de la superficie. Al reducir la imagen, los pixeles destinos serán promedio de los pixeles originales que representan. Al aumentar la imagen, se utilizará un filtro bilineal. En las arquitecturas amd64 y i686, se incluyen las rutinas MMX optimizadas que funcionarán mucho mas rápido que en otro tipo de equipos.
El tamaño es una secuencia de dos números que representa (width, height). Esta función solo opera con superficies de 24 o 32 bits. Se lanzará una excepción si la profundidad de color de la superficie original es menor a 24 bits.
Sintaxis:pygame.transform.smoothscale(Surface, (width, height), DestSurface =
None): return Surface
chop
Obtiene una copia de una imagen con un área interior eliminada. Extrae una porción de una imagen. Todos los pixels dentro del rectángulo dado se eliminarán. Las áreas diagonales (diagonales al rectángulo) se juntarán. La imagen original no se alterará por esta operación.
Nota: Si usted quiere cortar, esta función retorna la parte de la imagen sin el área del rectángulo; en su lugar puede imprimir con el rectángulo a una nueva superficie o copiar una subsurface.
sintaxis: pygame.transform.chop(Surface, rect): return Surface
laplacian
Busca los bordes en una superficie. Busca los bordes en una superficie usando el algoritmo laplacian.
sintaxis: pygame.transform.laplacian(Surface, DestSurface = None): return Surface
average_surfaces
Busca la superficie mas similar a partir de muchas otras. Toma una secuencia de
16
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
superficies y retorna la superficie con los colores mas parecidos a una dada.
sintaxis: pygame.transform.average_surfaces(Surfaces, DestSurface = None): return Surface
threshold
Encuentra cuales y cuantos pixels en una superficie están dentro del umbral de un color. Puede definir la superficie destino donde todos los pixeles se cambian a diff_color cuando no están dentro del umbral threshold. O se puede usar solo para contar los números de pixeles dentro del umbral si define change_return a False.
Cuando se define la tercera superficie, se usarán los colores de ella en lugar del tercer argumento color.
Puede definir un umbral, threshold, como (r,g,b,a), donde r, g, b pueden ser diferentes umbrales. De forma que si quiere puede usar un umbral r de 40 y un umbral b de 2.
sintaxis: pygame.transform.threshold(DestSurface, Surface, color, threshold = (0,0,0,0), diff_color = (0,0,0,0), change_return = True, Surface =None): return num_threshold_pixels
Pegale al Mono y Gana DineroPara terminar, el juego que nos toca hoy se llama: Pegale al Momo y Gana dinero. Por
si no tegusta otros nombres opcionales que se me ocurrieron, cambiando el fotograma (sprite) del mono:
– Pegale a Daniel Bermudez y la segunda parte es gratis. (vamos con 5US$ no hacemos mucho jjaja)
– Pegale al autor(Osea Yop) y te aseguro que te la devuelvo doble y ademas no subo la segunda parte... jajaja
– Pegale a los Alumnos que no suben los practicos y aprueva el curso...– etc...
No es que me guste maltratar a los animales, solo que este ejemplo sencillo de juego fue propuesto por el Autor de PyGame como demo (adaptando algunas cosas y agregando algunas lineas de comentario :P) el mismo (Ingles). Ademas como se ve abajo este no es woo que gran juego pero sirve para no aburrirse.
Modulos
Muy bien los modulos que esta ves vamos a requerir no son mas que pygame y el modulo sys para algunas algunas cosas:
17
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
import sys import pygame from pygame.locals import *
#preguntamos si esta disponible el modulo font if not(pygame.font): print 'No se encontro el modulo pygame.font' sys.exit(1)
En algunos paquetes no se encuentran disponibles algunos modulos de pygame devido a que faltan algunas librerias. Como ya dije y seguire diciendo en esencia pygame es un envoltorio de las librerias SDL (SDL_IMAGE, SDL_TTF, SDL_MIXER) y la no disponibilidad de algun modo ocurre por la falta de algunas de ella, aunque como minimo pygame requerira la librería SDL_IMAGEN, el resto son opcionales, aunque se incluyen en casi todos los paquetes de pygame.
Cargando imagenes
Normalmente cargamos la imagenes (archivos) directamente utilizando el metodo pygame.image.load() y le aplicamos algunas convercion (si se trata de un .png o .gif) o un colorkey si es una imagen de mapas de bits crudo (un bmp por ejemplo). Lo que conviene es crear una pequeña funcion que haga todo eso por nosotros:
def cargar_imagen(img_path, convert=False, alfa=None): """ Funcion extendida de PyGame que permite cargar imagenes """ try: imagen = pygame.image.load(img_path) except pygame.error, message: print 'No se pudo cargar la imagen: ', img_path raise SystemExit, message
if convert: if '.png' in img_path or '.gif' in img_path: imagen.convert_alpha() elif alfa != None: imagen.set_colorkey(alfa) else: imagen.set_colorkey(imagen.get_at((0, 0))) return imagen.get_rect(), imagen
Bien lo que hace nuestra funcion, es sencillo pero primero explico los argumentos que toma y que significa cada uno.
img_path: ruta de la imagen a cargar (string)convert: si se aplicara alguna conversion o colorkey (bool)alfa: se refiere al color que se usara como colorkey (tupla=(r,g,b))
Lo primero que hara la funcion sera intentar cargar la imagen, si no se encuentra la imagen (definida por img_path) lanzara una ecepcion. Lo siguiente es preguntar si se quiere convertir la imagen, si es un png o gif (no implemente nada que reconosca por formato de archivo, solo por el nombre del mismo, admito que la funcion no es perfecta y pueden ocurrir algunos errores al intentar determinar el tipo de archivo), si no es un png o gif, entonces se
18
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
tiene que tratar de una imagen sin canal alfa, tal como un (jpg, bmp, pcx, tga, etc )preguntara si se paso el colorkey (parametro definido por alfa), en caso contrario usara el color del primer pixel como colorkey. Por ultimo la funcion nos retornara la imagen y el rectangulo asociado.
Clases que representan los Objectos del Juego
Aquí crearemos las 2 clases para representar los objectos de juego (Si leyeron bien el juego apenas tiene 2 Objetos y a mi entender es mas sencillo de hacer que un Pong :P). Los dos objectos que vamos a usar seran: Mono al que le tendremos que pegar, Punio(Puño) con el que le pegaremos al mono (no quiero usar ñ en nombre de variables y obligar a codificar en ISO-Latin).
El Puño (Punio)
El unico atributo estraño que vemos es retardo el cual se lanzara cada ves que intentemos pegarle al mono, inabilitandonos durante un tiempo, la variable pegando es para consultar cada vez que se quiera lanzar un punio, el resto de los atributos son los tipicos (imagen y rectangulo).
Como la imagen representa una mano y no solo un puño, en el control de coliciones usamos otro rectangulo con tamaño reduciodo para darle mas realismo.
En sintesis el codigo nos quedara a como sigue:
class Punio: def __init__(self, imagen): #cargamos la imagen y definimos el rectangulo que manejara a la misma #usando nuestra nueva funcion que definimos anteriormente self.imagen, self.rect = cargar_imagen(img_path=imagen, convert=True) self.pegando = False self.retardo = 0 #tiempo de espera para poder pegar otra ves
def update(self, mono): """ Movemos el punio en base a la posicion del mouse ademas controlamos si le pegamos al mono """ #capturamos la posicion del mouse m_pos = pygame.mouse.get_pos() #centramos el mouse en el medio x, y la parte superio y self.rect.midtop = m_pos if self.pegando: self.rect.move_ip(5, 10)
#restaura el punio a la posicion inicial para poder segir pegando
19
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
if self.pegando and self.retardo <= 0: self.pegando = False self.retardo = 0 else: self.retardo -= 1
def pegar(self, mono): """ retorna True, si se pudo pegar, pero esto requiere que la bandera pegando esta en False y que por supuesto colicione con el mono """ if not(self.pegando): self.pegando = True self.retardo = 10 #rectangulo que se usara para control de colicion hitbox = self.rect.inflate(-5, -10) return hitbox.colliderect(mono.rect) else: return False
def drawn(self, surface): surface.blit(self.imagen, self.rect)
El Chimpance osea el Mono
La clase que maneja al mono no es tan sencilla como la que maneja el Punio. Esta clase moverá al mono de un lado a otro por la pantalla. Cuando el mono sea golpeado este girara por un momento.
Esta ves el metodo update (actualizar) del mono decide entre dos metodos _mover() o _girar() de acuerdo al estado de la variable self.dizzy que dice si el mono esta mareado o no, osea esta mareado si dizzy es mayor que 0, y no lo esta en caso contrario. Si el mono esta mareado este girara, en caso contrario se seguira moviendo.
El metodo adicional a mencionar es golpeado(), el mismo debe ser lanzado cada ves que el mono es golpeado con el puño. Bien explicado todo eso, que era lo importante el resto es lo de siempre, veamos como queda el codigo del mono:
class Mono: def __init__(self, imagen): #cargamos la imagen y definimos el rectangulo que manejara a la misma self.original, self.rect = cargar_imagen(img_path=imagen, convert=True) self.imagen = self.original.copy()
20
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
self.speed = 10 self.dizzy = 0 #tiempo que esta mareado hasta
def update(self): """ Actualiza el mono """ if self.dizzy > 0: self._girar() else: self._mover()
def golpeado(self): """ evento que deve ser llamado cuando este es golpeado """ self.dizzy = 1 self.imagen = self.original.copy() if self.speed < 0: self.imagen = pygame.transform.flip(self.imagen, True, False)
def _mover(self): """ mueve al momo por la pantalla, cambia la direcion del mismo cuando llega algun extremo """ self.rect.x += self.speed
#si el mono esta en el borde cambiamos la direcion if self.rect.x <= 0 or self.rect.right >= ANCHO: self.speed *= -1 self.imagen = pygame.transform.flip(self.imagen, True, False)
def _girar(self): """ hace girar al mono miestras este ester mareado """ centro = self.rect.center self.dizzy += 12 if self.dizzy >= 360: self.dizzy = 0 self.imagen = self.original.copy() else: self.imagen = pygame.transform.rotate(self.original, self.dizzy) self.rect = self.imagen.get_rect() self.rect.center = centro
def drawn(self, surface): surface.blit(self.imagen, self.rect)
Por ultimo el Mono, aunque no lo paresca es un primera aprocimacion a la IA (Inteligencia Artificial), pero ustedes diran ¿como? Si el mono lo unico que hace es moverse
21
Curso de: Programacion de Juegos con PyGame Por: Ricardo D. Quiroga – L2Radamanthys → [email protected]
de lado a lado y gira cuando esta mariado, joder ¿acaso eso es IA?
Si eso es IA, bueno el mono no es una maquina pensante, pero si miramos con detalle el mono es un tipico automata o mas explicitamente el mono termina siendo una maquina de estados, que responde a un grupo limitado de eventos los cuales son: (fue_golpeado, llego_a_algun_borde). Dichos eventos lanzas su respectivas reaciones, por ejemplo si se lanza el evento llego al borde el mono, canviara su direcion (que en el codigo se interpretaria como invertir la velocidad osea self.speed *= 1) mas adelante, seguramente en la segunda parte de este curso volveremos a tocar el tema.
Bucle Principal
Como siempre el bucle principal tiende al ser el mismo, inicializar pygame, definir la pantalla, instanciar las 2 clases (Mono y Punio) dentro del loop principal colocar los metodos update y drawn de cada objecto, colocar el titulo.
Seguro se estaran preguntando donde se implementara la parte de detecion de pulsaciones del mouse y coliciones, bueno la verdad como este codigo es demaciado sencillo es mas eficiente colocarlos en la secion donde normalmente se hace el manejo de eventos, nuestro bucle normal de manejo de eventos tiende a ser el siguiente:
for evento in pygame.event.get(): if evento.type == pygame.QUIT: loop =False
Ahora ademas de ello agregaremos un bucle if preguntando si se precion algun boton del mouse y en el implementaremos el control de coliciones de ambos objectos, por lo que nuestro bucle de manejos de evento quedaria como sigue:
for evento in pygame.event.get(): if evento.type == pygame.QUIT: loop =False elif evento.type == KEYDOWN and evento.key == K_ESCAPE: loop =False #controlo evento pegar elif evento.type == MOUSEBUTTONDOWN: if punio.pegar(mono): mono.golpeado()
Bueno eso es todo, para mas detalle miren el codigo, aquí les dejo una captura de pantalla del juego funcionando.
22