Intro PyGame Capitulo 5

22
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

description

Capitulo 5 del curso

Transcript of Intro PyGame Capitulo 5

Page 1: Intro PyGame Capitulo 5

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

Page 2: Intro PyGame Capitulo 5

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

Page 3: Intro PyGame Capitulo 5

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

Page 4: Intro PyGame Capitulo 5

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

Page 5: Intro PyGame Capitulo 5

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

Page 6: Intro PyGame Capitulo 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

Page 7: Intro PyGame Capitulo 5

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

Page 8: Intro PyGame Capitulo 5

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

Page 9: Intro PyGame Capitulo 5

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

Page 10: Intro PyGame Capitulo 5

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

Page 11: Intro PyGame Capitulo 5

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

Page 12: Intro PyGame Capitulo 5

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

Page 13: Intro PyGame Capitulo 5

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

Page 14: Intro PyGame Capitulo 5

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

Page 15: Intro PyGame Capitulo 5

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

Page 16: Intro PyGame Capitulo 5

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

Page 17: Intro PyGame Capitulo 5

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

Page 18: Intro PyGame Capitulo 5

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

Page 19: Intro PyGame Capitulo 5

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

Page 20: Intro PyGame Capitulo 5

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

Page 21: Intro PyGame Capitulo 5

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

Page 22: Intro PyGame Capitulo 5

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