Maclu
Usuario (Argentina)
Instrucciones para crear mundos paralelos Descubrimos el truco por casualidad, en nuestro propio edificio. Y como nos salió bien, empezamos a repetir la rutina en hogares ajenos, subidos a otros ascensores, con nuevas víctimas. Las bromas perfectas surgen de la nada, de un error o una impaciencia, y ésta fue una de las mejores. Tan original, y tan simple, que siempre nos pareció mentira que no existiera ya, que no fuese un clásico popular. Pero no lo era: lleva nuestra firma. De hecho, ésta será la primera vez que el truco tome estado público. Ojalá muchos adolescentes lean estas instrucciones, porque sólo hay una época en la vida en que nos atrevemos a poner en práctica ciertos juegos inútiles, absurdos y maravillosos. Yo, ahora mismo, con casi cuarenta años y esta panza, ya no podría. Sigo fumando idéntica hierba promotora, conservo en el corazón los mismos sueños, pero ya no estoy para andar corriendo de noche como un loco ni para pegar semejantes gritos de auxilio en la oscuridad. Me atraen los mundos paralelos igual que siempre, pero ahora prefiero concebirlos frente a un teclado, sentado tranquilamente en casa. Me convertí, no sé cuándo ni cómo, en un hijo de puta sedentario, en un mentiroso inmóvil. Pero no hablemos de problemas propios que ya no tienen solución. Mejor usemos este rato en explicar de qué modo podemos crear un mundo paralelo en el cerebro de un amigo ingenuo. Papel y lápiz. Ingredientes • Amigo que viva solo en un departamento, 1 • Cómplices, 3 • Banderín de club de fútbol, 1 • Marihuana, a gusto. Preparación Esperamos a que llegue la noche y nos presentamos a cenar, junto a dos cómplices, en el departamento de un amigo en común. Es fundamental que este amigo (al que llamaremos la Víctima) no resida en la planta baja ni en el primer piso del edificio. Tocamos el timbre de abajo y aguardamos a que la Víctima nos abra. Una vez en el ascensor, deberemos observar sus características, a fin de individualizar dónde aparece la numeración de cada piso durante en ascenso. Existen tres variantes. Los ascensores antiguos tienen la numeración sobre las paredes que vamos dejando atrás: 1, 2, 3, etcétera. Los modernos, en un visor digital sobre la botonera. Ambas ubicaciones son perfectas para nuestros planes. Si el visor estuviera demasiado alto (encima de la puerta de salida) el trabajo podría complicarse. En este ejemplo la Víctima reside en el 4ºB. Llegamos al piso en cuestión y, antes de que su morador nos haga entrar, utilizamos el rellano para saludarlo y —allí mismo— le hacemos entrega de un obsequio: el banderín de su club de fútbol preferido. —Mirá lo que te trajimos, Víctima —diremos con una sonrisa (es importante suplantar la palabra ‘víctima’ por el nombre real). El anfitrión nos agradecerá el regalo e intentará quedárselo, o guardarlo, pero nosotros fingiremos tener una idea mejor: —Ponélo acá, para que todo el mundo sepa que sos de Vélez —diremos, colgando el banderín en el picaporte exterior de la puerta. Sólo un 6% de las víctimas se niega a colgar el banderín. En general, los más reticentes son los hinchas de Racing (por un tema relacionado con la vergüenza). Pero la enorme mayoría acepta colgar el obsequio a la vista, sin sospechar que todo forma parte de un propósito siniestro. Durante las siguientes tres o cuatro horas actuaremos como cualquier grupo de amigotes que cena de noche en casa de alguien. Hablaremos de mujeres, de fútbol, de política y del sentido del universo. Jugaremos al póker, fumaremos marihuana, beberemos. Este paso es el más sencillo y, en términos técnicos, sólo busca alterar el sentido temporal de la Víctima. Durante los postres, el Cómplice Uno deberá individualizar las llaves del departamento. El manojo suele estar sobre un mueble, cerca de la puerta, o colgado. Cuando la sobremesa comience a decaer se iniciará la primera acción física importante, a la que denominamos El Éxodo. Para ello, y sin venir a cuento, el Cómplice Uno se levantará de la mesa y dirá: —Me pegó el bajón. Salgo un cacho a buscar alfajores —y se hará con las llaves del departamento, fingiendo el ademán de irse solo. —Buenísmo —dirá la Víctima—. Tenés un quiosco abierto sobre Scalabrini. En ese momento el Cómplice Dos deberá interrumpir la acción con esta frase: "¿Por qué no salimos todos, che, así nos aireamos un poco?". En cuestión de segundos, los cuatro tendrán que estar ubicados en el pasillo, del lado de afuera y a punto de cerrar la puerta, de la que cuelga el banderín. En ese instante hará su parlamento el Cómplice Tres: —Uy, me estoy cagando —dirá, tocándose la panza—, yo me quedo. ¿Está todo bien si van ustedes? —Todo bien —accederá la Víctima. Un minuto después, la primera parte del plan ya estará encarrilado. ¿Qué hemos conseguido hasta ahora? Recapitulemos: 1) tenemos a la Víctima en la calle, escoltada por dos de nosotros; 2) las llaves del departamento están en nuestro poder; y 3) conseguimos que un tercer cómplice se quede dentro. La noche nos sonríe. Consumación Mientras el trío se dirige al quiosco, el Cómplice Tres se ha quedado recluido en solitario. Por supuesto, nuestro colaborador no tiene deseos de cagar. Está allí para algo mucho más importante. En cuanto se sepa solo, el Cómplice Tres abrirá la puerta de entrada, quitará el banderín y, sin cerrar la puerta (porque no tiene llaves), deberá subir por las escaleras un piso más y colgar el banderín en el picaporte del 5ºB. Después, en silencio, deberá regresar al 4ºB y quedarse sentado a esperar el desenlace del truco. Mientras tanto, el grupo estará regresando de la calle con los alfajores. Entrarán los tres al edificio y subirán al ascensor entre bromas y monerías. Éste es un momento de gran importancia y coordinación general, alerta máxima. El Cómplice Uno será el encargado de apretar el botón del 5º piso (¡no del 4º!), mientras el Cómplice Dos se ubicará de tal modo que impida la vista numérica. La Víctima debe permanecer muy entretenida durante el viaje. En circunstancias normales, cualquier inquilino conoce, por costumbre, el tiempo exacto que tarda su elevador en llegar. Ahí es donde la droga blanda hace su parte. El porro provoca, entre otras muchas virtudes, la distorsión temporal y el anacronismo del usuario. La gente drogada siempre piensa que los ascensores tardan demasiado. La Víctima no sentirá el paso del tiempo, y llegará entonces al 5º piso con la certeza de que se encuentra en el 4º. Al apearse del ascensor observará también el banderín colgando de la puerta B, que creerá suya. Este dato quizá parezca nimio, pero es fundamental para que la Víctima reconozca una ambientación fidedigna. El Cómplice Dos saldrá del ascensor con las llaves en la mano, dispuesto a abrir la puerta. Entonces el Cómplice Uno dirá: —¡Esperen, esperen! Bajemos al 3º piso que les quiero mostrar algo. La Víctima, curiosa y en estado de gracia, bajará por las escaleras en compañía de los cómplices. Esto tiene que ocurrir entre las dos y las tres de la madrugada, en medio de un gran silencio. Una vez apostados en el verdadero 4º piso (la Víctima está convencida de que se trata del 3º), el Cómplice Uno se acercará al departamento B y, sin preámbulos, comenzará a golpear la puerta con los dos puños mientras gritará enloquecido: —¡Incendio! ¡Incendio! ¡Socorro! —en tanto, el Cómplice Dos festejará la gracia y se unirá a los golpes. La reacción de la Víctima es inmediata y está rigurosamente testada: hemos hecho este truco más de una docena de veces y siempre ocurrió lo mismo. La Víctima, señores, huye por las escaleras, para esconderse en el que sospecha su hogar. En el momento que la Víctima desaparece, el Cómplice Tres nos abre la puerta y entramos. Por supuesto, estábamos golpeando con los puños el 4ºB. Una vez los tres cómplices dentro, cerramos y seguimos gritando “¡Incendio, incendio!”, etcétera. La Víctima ya está en el 5º piso y ve que el banderín sigue colgado en la que cree su puerta. Se tantea los bolsillos y descubre que no tiene las llaves, pero es conocedor de que dentro ha quedado un amigo. Entonces le toca el timbre, con ansiedad, para que éste le abra. La Víctima tocará el timbre una vez, dos veces, a veces gritará “abrime, abrime”. No hará falta más. Cuando por fin se abra la puerta, aparecerá el vecino del 5ºB, en pijamas y enojadísimo. Se trata de un momento mágico e irrepetible. La creación real de un mundo paralelo en el cerebro de nuestro amigo. —Qué carajo te pasa, hijo de puta y la concha de tu hermana —suele ser, en siete de cada diez casos, la frase más utilizada entre los vecinos del 5ºB cuando son despertados a timbrazos en medio de la madrugada. Otros directamente estampan a la víctima contra la pared de una trompada, como nos ocurrió en dos oportunidades. En varios casos aparecen también otros propietarios e inquilinos, alertados por los gritos iniciales de incendio y socorro. Cuando ocurre tal cosa, la turba comienza a rodear a la Víctima con intención de venganza. No sabemos en qué momento todo se desmadra y se convierte en un linchamiento. Pero ocurre, siempre, pasados los seis minutos. Ese instante de descontrol vecinal es el momento indicado para la huída. Los tres cómplices llamarán al ascensor y egresarán del lugar en silencio, pero con gran velocidad. No antes, porque se perderían los sonidos de la paliza que está comenzando en el edificio. Y siempre es bueno oír el resultado de un buen truco. Una vez en la calle, es muy recomendable arrojar las llaves del departamento en una alcantarilla, imitando el final de un cuento muy famoso. Nuestra experiencia indica que más del 80% de las Víctimas no nos hablará durante el resto de su vida. El otro 20% puede tardar de dos a cinco años en aceptar nuestro perdón (que siempre redactamos vía email una semana más tarde de los hechos). Una vez que no hay heridas abiertas, cuando pasó ya mucho tiempo, a veces hemos preguntado a antiguas Víctimas qué sintieron, exactamente, en el momento que se abría la puerta del que creían su hogar y aparecía un señor enojado y en pijamas. —Es un flash —nos confiesan—. De repente dejás de hacer pie en la realidad, empezás a preguntarte si no será todo un sueño, tu cabeza se hunde en un mundo parecido al real… Nosotros nos quedamos en silencio y sonreímos para dentro. Y entonces las viejas víctimas suelen agregar: —De verdad… Es lo más impresionante que me pasó en la vida. Y eso nos basta para sentir que, otra vez, hemos mejorado la existencia de unos pobres diablos. Fuente

Ayer aburrido en casa me dieron ganas de ponerme a ver como programar juegos (algo que siempre tuve pendiente) y encontre un tutorial bastante interesante que explica paso a paso como crear un juego usando como ejemplo el viejo clasico "Pong". El tutorial es tan largo como meticuloso, pero tambien facil de seguir y practico. En 2 horas ya tenia el juego funcionando (bueno, convengamos que tampoco es ninguna maravilla... ). Antes de pasar al tutorial veamos que necesitamos: 1) tener Python instalado 2) tener Pygame instalado 3) algun editor de texto o alguna IDE de desarrollo para escribir el codigo 4) imagenes sonidos y fuentes del juego (las dejo mas abajo cuando comienze el ejemplo) Instalando Python y Pygame Desde Windows: Descargamos e instalamos en este orden: http://www.python.org/ftp/python/2.5.1/python-2.5.1.msi (python) http://www.pygame.org/ftp/pygame-1.7.1release.win32-py2.5.exe (pygame) Desde GNU/Linux En ubuntu sudo apt-get install python-pygame En Fedora sudo yum install python sudo yum install pygame En cuanto al IDE, en linux recomiendo Geany. Directorio base e imagenes Lo primero que necesitamos es un lugar de trabajo donde tengamos todos nuestros archivos y recursos necesarios organizados. Yo tengo un directorio llamado pygame donde voy metiendo una carpeta para cada proyecto que hago. Os dejo la carpeta inicial del juego que vamos a realizar en este tutorial. Contiene un fichero base(explicado mas abajo) y una carpeta images que contiene las imagenes y una fuente tipografica que usaremos para realixar nuestro juego. pong.zip El documento base Mi metodo de trabajo es tener todo organizado para que el codigo sea facil de leer, es por eso que antes de empezar voy a compartir mi plantilla base que podeis guardar como plantilla.py. A partir de ella escribo mis programas y es muy util para tener siempre a mano. #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos # Constantes # Clases # --------------------------------------------------------------------- # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- # --------------------------------------------------------------------- def main(): return 0 if __name__ == '__main__': main() Indice: 1) Importar e inicializar. 2) Creando una ventana. 3) Cargar imagenes. 4) Creando Sprites. 5) Moviendo Sprites. 6) Control del teclado. 7) Colisiones. 8) Inteligencia Artificial. 9) Sistema de puntuacion. 1. Importar e inicializar Lo primero que hay que hacer para trabajar con Pygame es importarlo e inicializarlo. Para importarlo basta con las lineas: import pygame from pygame.locals import * La primera linea importa pygame y la segunda carga las constantes para poder utilizar, por ejemplo, K_ESCAPE, en lugar de estar de tener que llamarla a traves del modulo Pygame. Una vez importado pasamos a inicializar. Se puede inicializar cualquiera de los modulos de Python por separado, pero para no complicarnos por ahora inicializaremos todo pygame con la linea: pygame.init() Esta se debe ejecutar antes de empezar a usar Pygame, un buen lugar es antes de llamar a la funcion main(), justo aqui: if __name__ == '__main__': pygame.init() main() 2. Creando una ventana Lo primero que necesitamos es definir las constantes del ancho y alto de la ventana, yo estas las defino como constantes porque de momento no vas a hacer cosas complicadas con cambios de resolucion ni nada por el estilo y tenerlas como constantes ayuda a trabajar con ellas desde cualquier clase o funcion. WIDTH = 640 HEIGHT = 480 Ahora pasamos a crear la ventana, se debe crear dentro de la funcion main(), es tan facil como lo siguiente: screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") La primera linea crea una ventana, date cuenta que la pasamos la tupla (WIDTH, HEIGHT) para definir las dimensiones de la ventana. La segunda linea sirve para definir el titulo de la ventana. El programa nos queda asi: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") return 0 if __name__ == '__main__': pygame.init() main() Si lo ejecutas te daras cuenta que se crea una ventana, pero esta desaparece al instante. Esto es debido a que necesitamos crear un bucle infinito, que sera el bucle del juego. Lo creamos justo despues de la creacion de la ventana. while True: pass Como vemos es un bucle infinito vacio que se ejecuta eternamente y hace que la ventana no se cierra, pero ¡sorpresa!, ahora no hay forma de cerrar la ventana si no es matando el proceso. La solucion consiste en que el bucle infinito compruebe si queremos cerrar la ventana y en ese caso que deje de ejecutarse. Sustituimos las dos lineas anteriores por: while True: for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) La primera linea del bucle recorre con un for pygame.event.get() que es una lista interna de Pygame con los eventos que se estan ejecutando. En la siguiente linea comprobamos que si el tipo de evento es igual a QUIT, es decir, que si le estamos dando a la crucesita de cerrar ventana. En caso de que si, invoca a sys.exit(0), para el que necesitamos haber importado el modulo sys, y cierra el programa. Ya tenemos la manera de crear una ventana y controlar cuando cerrarla. El codigo nos queda asi: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") while True: for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) return 0 if __name__ == '__main__': pygame.init() main() 3. Cargar imagenes Para cargar una imagen en Pygame vamos a usar una funcion que esta muy bien y facilita mucho el cargar imagenes al vuelo, pongo la funcion y la explicamos linea por linea: def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image La linea 1 define la funcion, recibe dos parametros el nombre/ruta del archivo y la segunda si tiene parte transparente (por defecto definida como falso). La linea 2 asigna a la variable imagen la imagen a traves de la funcion de Pygame pygame.image.load() si se puede sino, en la lineas 3 y 4 manejan el error y salen del programa. La linea 5 convierte la imagen al tipo interno de Pygame que hace que sea mucho mas eficiente. Las linea 6 es un condicional que controla si el parametro transparent es verdadero y en caso afirmativo ejecuta las lineas 7 y 8, la primera obtiene el color del pixel (0, 0) de la imagen (esquina superior izquierda) y la la linea 8 lo define como color transparente de la imagen. Es decir que si quieres una imagen con transparencia, el color que actua como transparente se toma del pixel superior izquierdo, asi que asegurate que este color no esta en la imagen. Por ultimo la linea 9 retorna la imagen despues de todo el proceso. El programa nos queda asi: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") while True: for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) return 0 if __name__ == '__main__': pygame.init() main() Poniendo un fondo Vamos a poner un fondo a nuestra ventana usando nuestra funcion load_image. Para cargar la imagen usaremos la siguiente linea, la ponemos justo antes de entrar en el bucle del juego: background_image = load_image('images/fondo.png') Como ves no definimos color transparente, porque para el fondo no es necesario. Ahora necesitamos “ponerla en la ventana” para ello dentro del bucle principal debemos definir que imagen poner y en que posicion: screen.blit(background_image, (0, 0)) Esto es lo que hace es poner la imagen la pantalla sobre las que habia, recibe dos parametros (en realidad algunos mas), la imagen a poner y en que lugar situar la esquina superior izquierda de la imagen, en este caso (0, 0), es decir, queremos que la esquina superior izquierda de la imagen este en la posicion (0, 0), con esto conseguimos que el fondo ocupe toda la ventana (si la imagen es mayor o superior a la ventana, claro esta). por ultimo, como ultima linea del bucle debemos incluir la siguiente linea: pygame.display.flip() Esto lo que hace es actualizar toda la ventana para que se muestren los cambios que han sucedido. El programa nos queda de la siguiente manera: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") background_image = load_image('images/fondo.png') while True: for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) screen.blit(background_image, (0, 0)) pygame.display.flip() return 0 if __name__ == '__main__': pygame.init() main() Bien con esto hemos conseguido poner una imagen de fondo y tener una forma facil de cargar imagenes en Pygame. Aconsejo guardar esto como plantilla base de Pygame, pues en casi todos los juegos necesitaremos cargar imagenes y establecer un fondo por defecto. 4. Creando Sprites Ahora empieza lo interesante y la escencia de los videojuegos en 2D, la creacion de Sprites. La clase Bola Para crear la pelota de nuestro Pong lo primero que necesitamos es la imagen de la pelota, descargar esta y guardarla como siempre en el directorio images. Ahora vamos a crear una clase para la pelota, que sera el Sprite de nuestra pelota, un Sprite es mas que una imagen, es una superficie que puede interactuar, moverse y demas. class Bola(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/ball.png", True) self.rect = self.image.get_rect() self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] La linea 1 crea la clase Bola que hereda los metodos de la clase pygame.sprite.Sprite, esto es muy importante debido a que contiene metodos necesarios para el manejo de Sprites. La linea 2 crea el metodo init que inicializa la clase. La linea 3 invoca al metodo init de la clase heredada, muy importante tambien y dificil de explicar para comenzar. La linea 4 ya nos suena mas y lo que hace es cargar con nuestra funcion load_image() la imagen de la pelota, como vemos tenemos puesto True porque la pelota si tiene zonas transparentes. La linea 5 es de las cosas mas utiles de pygame la funcion self.image.get_rect() obtiene un rectangulo con las dimensiones y posicion de la imagen (en este caso self.image) y se lo asignamos a self.rect Aqui hago un inciso para comentar que get_rect() tiene unos parametros muy utiles que podemos modificar para posicionar y redimensionar nuestra imagen, son los siguientes: top, left, bottom, right topleft, bottomleft, topright, bottomright midtop, midleft, midbottom, midright center, centerx, centery size, width, height w, h Asi que podemos acceder a los diferentes valores como self.rect.centerx que nos devuelve la posicion central de la pelota respecto al ancho de la pantalla, y asi con todos es cuestion de probarlos, pero mas o menos se entiende lo que devuelve cada uno. Lo mejor de todos ellos es que si cambias el valor de alguno el resto se actualiza. Aqui lo teneis mejor expliado. En la linea 6 y 7 usamos las propiedades de rect y con centerx y centery definimos el centro de la pelota en el centro de la pantalla. (aqui vemos porque pusimos WIDTH y HEIGHT como constantes globales). La linea 8 define la velocidad que queremos para la pelota, separamos la velocidad en dos, la velocidad en el eje x y la velocidad en el eje y, luego veremos porque. Poniendo una Bola en nuestro juego Bien una vez creada la clase Bola vamos a crear una bola, para ello como cuando cargamos el fondo, debemos cargarla antes del bucle del juego, con la siguiente linea: bola = Bola() Ahora solo debemos “ponerla” en nuestra ventana, ejecutando la siguiente sentencia, despues de la sentencia identica a la del fondo. screen.blit(bola.image, bola.rect) Como puedes ver ahora no le pasamos la imagen y en vez de unas coordenadas como en el fondo (entonces le pasamos (0, 0)) le pasamos el rect de la bola, con esto conseguiremos mas tarde que cada vez que movamos el rect, moveremos la pelota de sitio. Es muy importante “ponerla en pantalla despues y no antes que el fondo porque si pones la pelota y luego pones el fondo encima esta no se vera, como es logico. El programa nos queda asi: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- class Bola(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/ball.png", True) self.rect = self.image.get_rect() self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") background_image = load_image('images/fondo_pong.png') bola = Bola() while True: for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) screen.blit(background_image, (0, 0)) screen.blit(bola.image, bola.rect) pygame.display.flip() return 0 if __name__ == '__main__': pygame.init() main() Ahora tenemos una pelota en el centro de nuestra pantalla lo siguiente es conseguir que se mueva e interactue con los bordes. 5. Moviendo Sprites El metodo actualizar A continuacion aprenderemos a mover nuestra bola por la superficie y a calcular los rebotes, para ello emplearemos matematicas y fisica basica. def actualizar(self, time): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time La linea 1 define el metodo, recibe el parametro self (como siempre) y el parametro time que es el tiempo transcurrido, mas adelante lo explicamos. La linea 2 y 3 usa la fisica basica de espacio es igual a la velocidad por el tiempo (e = v*t), por tanto establecemos que el centro de nuestro rectangulo en x es el valor que tenia (self.rect.centerx) mas (+=) la valocidad a la que se mueve en el eje x (self.speed[0]) por (*) el tiempo transcurrido (time). Lo mismo se aplica al eje y en la linea 3. La linea 4, 5 y 6 establece que si la parte izquierda del rectangulo de la bola es menor o igual a 0 o mayor o igual a el ancho de la pantalla (WIDTH), es decir, que este en el extremo izquierdo o derecho, la velocidad de x (self.speed[0]) cambie de signo (-self.speed[0]) con esto conseguiremos que vaya hacia el otro lado. Las lineas 7, 8 y 9 es lo mismo pero en el eje y como se puede ver. Creando un reloj Ahora vamos a crear un reloj que controle el tiempo del juego, esto es importante para el movimiento, pues sabemos cuanto tiempo a pasado desde la ultima actualizacion de la pelota y con ello poder situarla en el espacio. clock = pygame.time.Clock() Esta linea va justo antes de entrar en el bucle del juego y sirve para crear el reloj con el que gestionar el tiempo. Ahora necesitamos saber cuanto tiempo pasa cada vez que se ejecuta una intereccion del bucle, para ello dentro del bucle ponemos como primera linea: time = clock.tick(60) El 60 que se le pasa como parametro es el framerate, con el nos aseguramos de que el juego no va a mas de la velocidad estipulada. Con ello conseguimos que el juego corra igual en todos los ordenadores. Por ultimo debemos actualizar la posicion de la bola antes de actualizarla en la ventana, es decir antes de los screen.blit. bola.actualizar(time) Como ves le pasamos el parametro time que es el tiempo que ha pasado desde la ultima vez que se ejecuto. El juego queda asi: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- class Bola(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/ball.png", True) self.rect = self.image.get_rect() self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] def actualizar(self, time): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") background_image = load_image('images/fondo_pong.png') bola = Bola() clock = pygame.time.Clock() while True: time = clock.tick(60) for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) bola.actualizar(time) screen.blit(background_image, (0, 0)) screen.blit(bola.image, bola.rect) pygame.display.flip() return 0 if __name__ == '__main__': pygame.init() main() Ya nuestra pelota se mueve y rebota contra las paredes. No es la panace del movimiento, pero esta bien. 6. Control del teclado Bien ya tenemos nuestra pelota que se mueve como loca por toda la pantalla. El siguiente paso es crear un nuevo Sprite para las palas, este a diferencia de la bola no se movera solo sino que lo controlaremos nosotros por el teclado. Creando el Sprite Pala La clase Pala es casi identica a la clase Bola, salvo por unos pequeños cambios: class Pala(pygame.sprite.Sprite): def __init__(self, x): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/pala.png") self.rect = self.image.get_rect() self.rect.centerx = x self.rect.centery = HEIGHT / 2 self.speed = 0.5 Como vemos es identica salvo que ahora le pasamos el parametro x para usarlo en self.rect.centerx, esto es debido a que necesitamos dos palas una en la parte izquierda y otra en la derecha, con el parametro x definimos a que altura del eje x queremos colocar el Sprite. Otro cambio es la velocidad, como la pala del Pong solo se mueve en el eje y no definimos velocidad para el eje x. para crear la pala del jugador ya sabeis, debajo de donde habeis creado la bola poneis la siguiente linea: pala_jug = Pala(30) La llamo pala_jug porque habra otra que sera la pala_cpu que sera la que maneje el ordenador. Como ves le paso como valor de x = 30, esto quiere decir que centerx estara a 30 px del borde derecho de la ventana. Por ultimo dentro del bucle lo de siempre, despues de actualizar el fondo y la bola actualizamos la pala: screen.blit(pala_jug.image, pala_jug.rect) Moviendo la pala Para mover la pala definimos el metodo mover dentro de la clase Pala: def mover(self, time, keys): if self.rect.top >= 0: if keys[K_UP]: self.rect.centery -= self.speed * time if self.rect.bottom <= HEIGHT: if keys[K_DOWN]: self.rect.centery += self.speed * time Recibe los parametros self y time como el metodo actualizar de la bola y ademas recibe el parametro keys que luego definiremos y que es una lista con el valor booleano de las teclas pulsadas. La linea 2 y 5 comprueban que la parte superior (en el caso de la linea 2) de la pala sea mayor o igual a 0 y que la parte inferior de la pala (linea 5) sea menor o igual que que la altura de la ventana. Resumiendo comprueban que la pala no se sale de la ventana. La linea 3 comprueba si la constante K_UP de keys es 1, lo que querria decir que tenemos presionada la tecla de la flecha hacia arriba del teclado. La linea 5 en caso de tener la tecla presionada disminuye el valor de centery haciendo que la pala se mueva hacia arriba. La linea 6 y 7 hacen lo mismo, pero para abajo y aumentando el valor de centery. Ahora solo debemos saber que teclas se estan pulsando creando la variable keys, esto se consigue añadiendo la siguiente linea en el bucle principal, justo despues de comprobar el tiempo transcurrido con time. keys = pygame.key.get_pressed() Esto nos devuelve las teclas pulsadas en una lista como explicamos arriba. Por ultimo debemos llamar al metodo mover en el bucle justo despues de actualizar la bola, con la linea: pala_jug.mover(time, keys) El codigo nos queda asi: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- class Bola(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/ball.png", True) self.rect = self.image.get_rect() self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] def actualizar(self, time): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time class Pala(pygame.sprite.Sprite): def __init__(self, x): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/pala.png") self.rect = self.image.get_rect() self.rect.centerx = x self.rect.centery = HEIGHT / 2 self.speed = 0.5 def mover(self, time, keys): if self.rect.top >= 0: if keys[K_UP]: self.rect.centery -= self.speed * time if self.rect.bottom <= HEIGHT: if keys[K_DOWN]: self.rect.centery += self.speed * time # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") background_image = load_image('images/fondo_pong.png') bola = Bola() pala_jug = Pala(30) clock = pygame.time.Clock() while True: time = clock.tick(60) keys = pygame.key.get_pressed() for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) bola.actualizar(time) pala_jug.mover(time, keys) screen.blit(background_image, (0, 0)) screen.blit(bola.image, bola.rect) screen.blit(pala_jug.image, pala_jug.rect) pygame.display.flip() return 0 if __name__ == '__main__': pygame.init() main() Ya tenemos nuestra pala que podemos controlar con el teclado, pero la bola como vemos para olimpicamente de ella y la atraviesa. A continuacion aprenderemos a crear colisiones entre Sprites 7. Colisiones Tenemos una pelota que se mueve como loca y una pala que va para arriba y para abajo a nuestra voluntad, es hora de que se den cuenta que estan en el mismo mundo. Vamos a crear colisiones entre los dos sprites. Actualizando el metodo actualizar de la clase Bola Saber si un Sprite colisiona con otro es muy facil en Python, basta con ejecutar el siguiente metodo: pygame.sprite.collide_rect(objeto1, objeto2) Esto comprueba si el rectangulo del Sprite objeto1 esta en contacto con el rectangulo de objeto2, por lo que es tan facil como mejorar el metodo actualizar de la clase Bola para que lo tenga en cuenta: def actualizar(self, time, pala_jug): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time if pygame.sprite.collide_rect(self, pala_jug): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time Como vemos todo igual, salvo que ahora recibe un parametro mas que es pala_jug por el que pasaremos el Sprite con el que queremos comprobar si colisiona, en este caso pala_jug. Luego al final añadimos 3 lineas con un nuevo condicional con el que comprobamos si la pelota choca contra la pala, en caso afirmativo cambiamos la direccion de la bola como cuando choca con el borde izquierdo de la ventana. Por ultimo solo actualizamos la llamada a la funcion actualizar que hacemos desde el bucle para pasarle la pala_jug. bola.actualizar(time, pala_jug) Y con esto ya hemos logrado que nuestra bola detecte si ha chocado contra la pala del jugador en caso afirmativo “rebota”. Asi nos queda el codigo: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- class Bola(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/ball.png", True) self.rect = self.image.get_rect() self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] def actualizar(self, time, pala_jug): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time if pygame.sprite.collide_rect(self, pala_jug): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time class Pala(pygame.sprite.Sprite): def __init__(self, x): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/pala.png") self.rect = self.image.get_rect() self.rect.centerx = x self.rect.centery = HEIGHT / 2 self.speed = 0.5 def mover(self, time, keys): if self.rect.top >= 0: if keys[K_UP]: self.rect.centery -= self.speed * time if self.rect.bottom <= HEIGHT: if keys[K_DOWN]: self.rect.centery += self.speed * time # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") background_image = load_image('images/fondo_pong.png') bola = Bola() pala_jug = Pala(30) clock = pygame.time.Clock() while True: time = clock.tick(60) keys = pygame.key.get_pressed() for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) bola.actualizar(time, pala_jug) pala_jug.mover(time, keys) screen.blit(background_image, (0, 0)) screen.blit(bola.image, bola.rect) screen.blit(pala_jug.image, pala_jug.rect) pygame.display.flip() return 0 if __name__ == '__main__': pygame.init() main() En el siguiente paso crearemos la pala del jugador de la CPU y la dotaremos de Inteligencia Artificial para que golpee la pelota. 8. Inteligencia Artificial En el siguiente tema no introduciremos ningun concepto nuevo de Pygame, pero si usaremos un poco de todo lo usado en los anteriores tutoriales para crear la pala que controlara el ordenador, se podria decir que es una Inteligencia Artificial muy basica. Creando la pala de la CPU Esto se hace igual que como creamos la pala_jug o la bola, ponemos la siguiente linea en la funcion principal debajo de pala_jug. pala_cpu = Pala(WIDTH - 30) En su momento pala_jug le pasamos como parametro el valor 30 que queria decir que el centerx estaba a 30 pixeles de el borde izquierdo, ahora le pasamos WIDTH - 30, es decir a 30 pixeles del borde derecho. Tambien debemos añadir la linea: screen.blit(pala_cpu.image, pala_cpu.rect) En el bucle principal, debajo de los demas blit, como siempre. Con esto ya tenemos nuestro Sprite puesto en pantalla. Ahora vamos a hacer que detecte tambien las colisiones, para ello volvemos a actualizar el metodo actualizar de la clase Bola: def actualizar(self, time, pala_jug, pala_cpu): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time if pygame.sprite.collide_rect(self, pala_jug): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if pygame.sprite.collide_rect(self, pala_cpu): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time Como podemos ver añadimos otro parametro al metodo pala_cpu que servira para añadirle el Sprite pala_cpu como hicimos con pala_jug y añadimos las tres ultimas lineas que son identicas a las anteriores salvo que ahora para pala_cpu. Recordad pasarle el parametro nuevo a la linea que llama a la funcion actualizar de la bola en el bucle del juego. Asi: ball.update(time, pala_jug, pala_cpu) Dotando de Inteligencia Artificial a la Pala Bueno ahora viene lo interesante, lograr que la pala se mueva para golpear la bola, esto se hace definiendo otro metodo en la clase Pala al metodo lo llamare ia. def ia(self, time, ball): if ball.speed[0] >= 0 and ball.rect.centerx >= WIDTH/2: if self.rect.centery < ball.rect.centery: self.rect.centery += self.speed * time if self.rect.centery > ball.rect.centery: self.rect.centery -= self.speed * time En la linea 1 vemos que recibe como siempre self y time y aparte recibe ball que es la bola, es necesario pues el metodo necesita conocer donde esta la bola. En la linea 2 comprobamos que ball.speed[0] >= 0, es decir, que la velocidad en el eje x de la pelota sea positiva, es decir, que la pelota se este moviendo hacia la derecha (hacia la pala de la cpu) y tambien comprueba que ball.rect.centerx >= WIDTH/2 es decir que el centro x de la pelota sea mayor o igual que el centro del tablero, es decir, que la pelota este en el campo de la cpu. Por tanto la linea 2 es un condicional que comprueba que la pelota vaya hacia donde esta la pala de la cpu y que este en su campo, sino, que no se mueva. Esto se hace para que la CPU no sea invencible y no llegue a todas las pelotas. La linea 3 comprueba si el centery de la pelota es menor que el centery de la bola, es decir si la pala esta mas arriba que que la pelota en cullo caso, ejecuta la linea 4 que mueve la pala de la cpu hacia abajo. Las lineas 5 y 6 hacen lo mismo, pero a la inversa como se ve a simple vista. Ahora solo nos queda llamar a la ia junto con las llamadas actualizar de la bola y mover de la pala_cpu. pala_cpu.ia(time, bola) El codigo nos queda de la siguiente manera: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- class Bola(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/ball.png", True) self.rect = self.image.get_rect() self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] def actualizar(self, time, pala_jug, pala_cpu): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time if pygame.sprite.collide_rect(self, pala_jug): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if pygame.sprite.collide_rect(self, pala_cpu): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time class Pala(pygame.sprite.Sprite): def __init__(self, x): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/pala.png") self.rect = self.image.get_rect() self.rect.centerx = x self.rect.centery = HEIGHT / 2 self.speed = 0.5 def mover(self, time, keys): if self.rect.top >= 0: if keys[K_UP]: self.rect.centery -= self.speed * time if self.rect.bottom <= HEIGHT: if keys[K_DOWN]: self.rect.centery += self.speed * time def ia(self, time, ball): if ball.speed[0] >= 0 and ball.rect.centerx >= WIDTH/2: if self.rect.centery < ball.rect.centery: self.rect.centery += self.speed * time if self.rect.centery > ball.rect.centery: self.rect.centery -= self.speed * time # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") background_image = load_image('images/fondo_pong.png') bola = Bola() pala_jug = Pala(30) pala_cpu = Pala(WIDTH - 30) clock = pygame.time.Clock() while True: time = clock.tick(60) keys = pygame.key.get_pressed() for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) bola.actualizar(time, pala_jug, pala_cpu) pala_jug.mover(time, keys) pala_cpu.ia(time, bola) screen.blit(background_image, (0, 0)) screen.blit(bola.image, bola.rect) screen.blit(pala_jug.image, pala_jug.rect) screen.blit(pala_cpu.image, pala_cpu.rect) pygame.display.flip() return 0 if __name__ == '__main__': pygame.init() main() Ahora el juego es jugable, pero no tiene ni sistema de puntuacion, ni sonidos ni nada de nada. Pronto lo solucionaremos. 9. Sistema de puntuacion En los dos siguientes temas vamos a aprender a manejar fuentes tipograficas en Pygame. Crearemos un sistema de puntuacion para nuestro Pong, consistira en dos marcadores para cada jugador que si consigues meter un punto al rival aumenta en uno. ¿Sencillo no? Vamos alla. Creando un sistema de puntuacion Lo primero es crear un sistema que controle los puntos, es decir, comprobar si la bola traspasa las palas y toca el borde de la ventana que tiene detras. Esto lo controlara como siempre la bola y su metodo actualizar. def update(self, time, pala_jug, pala_cpu, puntos): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0: puntos[1] += 1 if self.rect.right >= WIDTH: puntos[0] += 1 if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time if pygame.sprite.collide_rect(self, pala_jug): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if pygame.sprite.collide_rect(self, pala_cpu): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time return puntos Como vemos le añadimos un nuevo parametro llamado puntos, puntos es una lista que contiene los puntos de los dos jugadores en el el puntos[0] los puntos del jugador y en el puntos[1] los puntos de la cpu. Luego añadimos las lineas de la 5 a la 8 que controlan si la parte izquierda de la pelota (linea 5) toca el el borde izquierdo de la ventana en cuyo caso aumenta puntos[1] (el marcador de la cpu) en 1 (linea 6). La lineas 7 y 8 hacen lo mismo, pero a la inversa. Por ultimo al final del metodo se retorna puntos, necesario para almacenarla en una variable. Ahora debemos crear la lista puntos en nuestra funcion principal, yo la he creado justo antes de entrar en el bucle del juego: puntos = [0, 0] Ahora tenemos que modificar la llamada a bola.actualizar dentro del bucle del juego para pasarle nuestra lista puntos y recuperarla de nuevo con los posibles nuevos valores. puntos = bola.actualizar(time, pala_jug, pala_cpu, puntos) Por lo que el codigo nos queda asi: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 # Clases # --------------------------------------------------------------------- class Bola(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/ball.png", True) self.rect = self.image.get_rect() self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] def actualizar(self, time, pala_jug, pala_cpu, puntos): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0: puntos[1] += 1 if self.rect.right >= WIDTH: puntos[0] += 1 if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time if pygame.sprite.collide_rect(self, pala_jug): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if pygame.sprite.collide_rect(self, pala_cpu): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time return puntos class Pala(pygame.sprite.Sprite): def __init__(self, x): pygame.sprite.Sprite.__init__(self) self.image = load_image("images/pala.png") self.rect = self.image.get_rect() self.rect.centerx = x self.rect.centery = HEIGHT / 2 self.speed = 0.5 def mover(self, time, keys): if self.rect.top >= 0: if keys[K_UP]: self.rect.centery -= self.speed * time if self.rect.bottom <= HEIGHT: if keys[K_DOWN]: self.rect.centery += self.speed * time def ia(self, time, ball): if ball.speed[0] >= 0 and ball.rect.centerx >= WIDTH/2: if self.rect.centery < ball.rect.centery: self.rect.centery += self.speed * time if self.rect.centery > ball.rect.centery: self.rect.centery -= self.speed * time # --------------------------------------------------------------------- # Funciones # --------------------------------------------------------------------- def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") background_image = load_image('images/fondo_pong.png') bola = Bola() pala_jug = Pala(30) pala_cpu = Pala(WIDTH - 30) clock = pygame.time.Clock() puntos = [0, 0] while True: time = clock.tick(60) keys = pygame.key.get_pressed() for eventos in pygame.event.get(): if eventos.type == QUIT: sys.exit(0) puntos = bola.actualizar(time, pala_jug, pala_cpu, puntos) pala_jug.mover(time, keys) pala_cpu.ia(time, bola) screen.blit(background_image, (0, 0)) screen.blit(bola.image, bola.rect) screen.blit(pala_jug.image, pala_jug.rect) screen.blit(pala_cpu.image, pala_cpu.rect) pygame.display.flip() return 0 if __name__ == '__main__': pygame.init() main() Apendice: contenido propio Bien, hasta aca fue el tutorial de iniciacion de Loser Juegos, pero este deja pendiente el mostrar el marcador en el juego, asi que va un pequeño apendice con la solucion que encontre para esto. Tambien agrego como hacer para que cada vez que se anota un tanto, la pelota vuelva a salir del centro, y como hacer para quitar el juego presionando la una tecla, en este caso, la q. Tanteador en pantalla Lo primero que tenemos que hacer es crear una clase Text que use el modulo font de pygame. De todas formas formas, antes de ir a esta clase, voy a declarar una contanste para declarar el color blanco que utilizaremos para el texto mas adelante: white = (255,255,255) De esta forma, las primeras lineas del programa ahora nos quedan asi: #!/usr/bin/env python # -*- coding: utf-8 -*- # Modulos import sys, pygame from pygame.locals import * # Constantes WIDTH = 640 HEIGHT = 480 white = (255,255,255) Ahora si, en la seccion de clases de nuestro codigo creamos la clase Text: class Text (pygame.font.Font): def __init__ (self, FontName = None, FontSize = 30): pygame.font.init() self.font = pygame.font.Font(FontName, FontSize) self.size = FontSize def render (self, surface, text, color, pos): text = unicode(text, "UTF-8") x, y = pos for i in text.split("r"): surface.blit(self.font.render(i, 1, color), (x, y)) y += self.size y antes de ingresar al bucle del juego instanciamos la clase Text: text = Text() y a la hora de actualizar la pantalla agregamos la orden para que se actualice tambien el resultado en el marcador: numero1=str(puntos[1]) text.render(screen, "P1: "+numero1, white, (20, 0)) numero0=str(puntos[0]) Salir con una tecla Este paso es bastante sencillo, simplemente basta con agregar al metodo mover de la clase Pala las siguitenes lineas: if keys[K_q]: sys.exit(0) Reponiendo la bola del medio Para lograr que luego de cada punto la bola salga del medio, simplemente editamos el metodo actualizar de la clase Bola, y modificamos las lineas en que le decimo que agregue un punto para que reubique la bola en el centro: if self.rect.left <= 0: puntos[0] += 1 self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [-0.5, 0.5] if self.rect.right >= WIDTH: puntos[1] += 1 self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] Finalmente, el codigo completo con las modificaciones mias quedaria asi: import pygame from pygame.locals import * import sys, os if not pygame.font: print 'Warning, fonts disabled' # Constantes WIDTH = 640 HEIGHT = 480 white = (255,255,255) # Clases class Bola(pygame.sprite.Sprite, pygame.font.Font): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = load_image("img/ball.png", True) self.rect = self.image.get_rect() self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] def actualizar(self, time, pala_jug, pala_cpu, puntos): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0: puntos[0] += 1 self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [-0.5, 0.5] if self.rect.right >= WIDTH: puntos[1] += 1 self.rect.centerx = WIDTH / 2 self.rect.centery = HEIGHT / 2 self.speed = [0.5, -0.5] if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed [0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time if pygame.sprite.collide_rect(self, pala_jug): self.speed[0] = -self.speed[0] self.rect.centery += self.speed[0] * time if pygame.sprite.collide_rect(self, pala_cpu): self.speed[0] = -self.speed[0] self.rect.centery += self.speed[0] * time return puntos class Pala (pygame.sprite.Sprite): def __init__(self, x): pygame.sprite.Sprite.__init__(self) self.image = load_image ("img/pala.png") self.rect = self.image.get_rect() self.rect.centerx = x self.rect.centery = HEIGHT / 2 self.speed = 0.5 def mover (self, time, keys): if keys[K_q]: sys.exit(0) if self.rect.top >= 0: if keys[K_UP]: self.rect.centery -= self.speed * time if self.rect.bottom <= HEIGHT: if keys[K_DOWN]: self.rect.centery += self.speed * time def ia (self, time, ball): if ball.speed[0] >= 0 and ball.rect.centerx >= WIDTH / 2: if self.rect.centery < ball.rect.centery: self.rect.centery += self.speed * time if self.rect.centery > ball.rect.centery: self.rect.centery -= self.speed * time class Text (pygame.font.Font): def __init__ (self, FontName = None, FontSize = 30): pygame.font.init() self.font = pygame.font.Font(FontName, FontSize) self.size = FontSize def render (self, surface, text, color, pos): text = unicode(text, "UTF-8") x, y = pos for i in text.split("r"): surface.blit(self.font.render(i, 1, color), (x, y)) y += self.size # --------------------------------------------------------------------- # --------------------------------------------------------------------- # Funciones def load_image(filename, transparent=False): try: image = pygame.image.load(filename) except pygame.error, message: raise SystemExit, message image = image.convert() if transparent: color = image.get_at((0,0)) image.set_colorkey(color, RLEACCEL) return image # --------------------------------------------------------------------- # --------------------------------------------------------------------- def main(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pruebas Pygame") background_imagen = load_image('img/fondo_pong.png') bola = Bola() clock = pygame.time.Clock() pala_jug = Pala(30) pala_cpu = Pala(610) text = Text() puntos = [0 , 0] while True: time = clock.tick(60) keys = pygame.key.get_pressed() for eventos in pygame.event.get(): if eventos.type == quit: sys.exit(0) bola.actualizar(time, pala_jug, pala_cpu, puntos) pala_jug.mover(time, keys) pala_cpu.ia (time, bola) screen.blit(background_imagen, (0, 0)) numero1=str(puntos[1]) text.render(screen, "P1: "+numero1, white, (20, 0)) numero0=str(puntos[0]) text.render(screen, "Cpu: "+numero0, white, (560, 0)) screen.blit(bola.image, bola.rect) screen.blit(pala_jug.image, pala_jug.rect) screen.blit(pala_cpu.image, pala_cpu.rect) pygame.display.flip() return 0 if __name__ == '__main__': main()
Delta Conky Lite, es un theme para el monitor de sistema Conky, simple, pero elegante. La instalación de conky suele ser muy simple, ya que generalmente se encuentra en los repositorios de las distros mas conocidas: Para Ubuntu: sudo apt-get install conky Para Arch: sudo pacman -S conky Este tema utiliza 4 fuentes, las cuales son necesarias para lograr el aspecto que se muestra en los screenshot 1) Zero threes 2) StyleBats 3) OpenLogos 4) PizzaDude Bullets El archivo de configuración se descarga de aquí. Una vez descargado, descomprimimos, y copiamos el archivo .conkyrc al home de nuestro usuario. Edito y agrego: Para que aparezca también el calendario de abajo, tenemos dos opciones: La primera es ejecutar: conky -c conkycalendar & conky La segunda, ejecutar el archivo .conky_start.sh que viene dentro del archivo comprimido donde estan los archivos de configuración del tema en lugar de ejecutar conky (este archivo solo invoca a conky con los comandos necesarios para mostrar el calendar. Fuente: gnome-look.org
Mi principal preocupacion a la hora de trabajar es tener espacio visual suficiente para acomodar todo lo que necesito: mails, notas, paginas web, IDE de desarrollo, etc. Es muy importante para mi tener todo a mano, para eso me resulta indispensable contar con 2 monitores. Pero dos monitores que muestran exactamente lo mismo no es de gran ayuda. Por suerte existe la posibilidad de configurar los dos monitores en modo extendido, es decir, ambos monitores muestran mitades de un gran escritorio. En Windows y en Ubuntu no representa gran problema lograr esta configuración, pero en otras distribuciones suele ser un poco mas complicado. En esta ocasión mostrare como logre configurar dos monitores en modo extendido en Manjaro Linux. En Manjaro al conectar un segundo monitor (en este caso se muestra la configuración en una netbook con un monitor conectado a una salida VGA). Como vemos en la imagen por defecto nos permite activar ambos monitores, pero no en modo extendido. Para poder configurar ambos monitores recurriremos a la querida y entrañable consola, y al comando xrandr. Como vemos el comando nos devuelve nuestros monitores, sus posibles resoluciones de pantalla, y frecuencia. Para activar ambos monitores executamos: xrandr –output LVDS1 –auto –output VGA1 –auto –left-of LVDS1 De esta forma activamos ambos monitores con resolución automatica y ademas le decimos que el monitor externo (VGA1) se ubique a la izquierda del monitor de la netbook (LVDS1). Tambien podemos establecer la resolución que queramos agregando el parametro mode de la siguiente manera: xrandr –output LVDS1 –mode 12800×800 –output VGA1 –mode 800×600 –left-of LVDS1 De esta forma obtendremos un resultado similiar al siguiente:

¿otra vez vas a hablar de lo mismo? Sip... devuelta a lo mismo. Si leyeron hace algunos días la "solución" que propuse para conectarnos a una vpn sin perder la salida a internet, quizas no lo notaron, pero esa solucion puede ser un tanto tediosa si hay que utilizarla tras cada reinicio, tras cada reconexion (sobre todo con DHCP). Despues de dos semanas utilizando el sistema ya descripto, tuve la necesidad de automatizar el proceso, de modo que escribi un script en bash (al que llame vpn.sh) que detecta cuando estamos conectados, y cuando, estando conectados, no tenemos acceso a la VPN y a internet al mismo tiempo, y ejecuta el comando route para permitirnos navegar libremente mientras estamos dentro de la vpn. #!/bin/bash log='/home/usuario/logVPN.txt' VPNACTIVA=`route | grep -c ppp0` HORA=`date` if [ $VPNACTIVA = "0" ]; then echo 'no hay vpn' > $log echo 'IP = 011.110.110.110' >> $log echo 'Ultima Ejecucion: ' $HORA >> $log exit else IPACTIVA=`ifconfig ppp0 | grep 192.168.0. | cut -c14-26` IP=`cat $log |grep IP | cut -c6-20` fi if [[ $IPACTIVA != $IP ]]; then IP=`ifconfig ppp0 | grep 192.168.0. | cut -c14-26` route add -net 192.168.0.0 netmask 255.255.255.0 gw $IP dev ppp0 echo 'IP =' $IP > $log echo 'Ultima Ejecucion '$HORA >> $log exit else echo 'comprobado: '$HORA >> $log exit fi Pero aún teniendo este script seria tedioso ejecutarlo cada vez que necesitmos ver si la conexión esta correctamente configurada, de modo que decidi agregarla al crontab de root para que corra automaticamente cada 3 minutos. De esta manera, cuando haya alguna interrupción de la conexión con la vpn, cuando el script se vuelva a ejecutar, estaremos correctamente conectados nuevamente. Para agregar el script a crontab nos logeamos como root y ejecutamos: crontab -e Eso nos va a abrir el archivo crontab para que lo editemos, o en caso de que no lo tengamos, creara uno nuevo. Dentro del archivo crontab escribimos lo siguiente: SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/ # run-parts */3 * * * * sh /home/usuario/vpn.sh Bien, hasta ahi una explicación corta de como automatizar el proceso, ahora hago una explicación mas "detallada" para quienes quieran entender un poco mejor lo que hice. Yapa Explicación del script Comienzo estableciendo 3 variables: #!/bin/bash log='/home/usuario/logVPN.txt' VPNACTIVA=`route | grep -c ppp0` HORA=`date` log='/home/usuario/logVPN.txt': Indico el archivo donde voy a ir guardando los Log de las distintas corridas del script. VPNACTIVA=`route | grep -c ppp0`: El valor de VPNACTIVA sera 0 solo si no existe interface ppp0, osea, cuando no estemos conectados a la VPN. Si no recuerdan por que es ppp0 visiten el anterior post y sabran ver que interface les corresponde ingresar a ustedes en su lugar. HORA=`date`: Guardo la hora actual del sistema para saber cual fue la ultima corrida del script. if [ $VPNACTIVA = "0" ]; then echo 'no hay vpn' > $log echo 'IP = 011.110.110.110' >> $log echo 'Ultima Ejecucion: ' $HORA >> $log exit else IPACTIVA=`ifconfig ppp0 | grep 192.168.0. | cut -c14-26` IP=`cat $log |grep IP | cut -c6-20` fi Luego comparo el valor de VPNACTIVA, si es 0, significa que no tenemos conexión a la VPN, reemplazo el archivos de log introduciendo una IP falsa que luego voy a usar para otras comparaciones. Si VPNACTIVA es distinto de 0, significa que tenemos conexión a la VPN, le damos a la variable IPACTIVA el valor de la IP que nos dío la VPN y a IP el valor de IP que tengamos en nuestro archivo de log. if [[ $IPACTIVA != $IP ]]; then IP=`ifconfig ppp0 | grep 192.168.0. | cut -c14-26` route add -net 192.168.0.0 netmask 255.255.255.0 gw $IP dev ppp0 echo 'IP =' $IP > $log echo 'Ultima Ejecucion '$HORA >> $log exit else echo 'comprobado: '$HORA >> $log exit fi En el siguiente paso comparamos la IPACTIVA con la IP. Si son distintas corre el comando route con la IP que tenemos en ese momento y la guarda en el archivo de log. Esto es asi por que si IPACTIVA y IP son distintas puede significar dos cosas: 1) Que en la corrida anterior la VPN este desactivada y la ip que figura en el log sea la ip falsa que forzamos. 2) Que haya habido una reconexión a la VPN y el servidor nos haya cambiado nuestra IP, siendo necesario actualizar esta información en el route. Si por el contrario las dos ip son iguales, simplemente agrega la hora de comprobación en el archivo de log. crontab Del crontab solo dire lo siguiente: cuando ejecuten crontab -e se abrira el editor predetarminado, generalmente es vi y para los queno estan acostumbrados es un tanto complicado. Para todos ellos recomiendo definir como editor a nano de la siguiente manera: EDITOR=nano export EDITOR No entrare mas en detalle con respecto a este tema por que les recomiendo que le den una leidita a la wiki de cron.

¿Configurar la VPN sin perder internet? ¿de que estas hablando Willi’s? Muy probablemente te haya sucedido que al conectarte a una VPN solo tengas conexión a los equipos que se encuentran dentro de ella. Esto pasa incluso cuando se configura una VPN desde Windows, o tenemos acceso completo a internet, o tenemos acceso a la VPN y los equipos que hay en ella. Sin embargo, en Windows la solución es muy simple, solo se le dice al sistema que la conexión a la VPN solo se use para acceder a los que equipos que estan dentro de ella, y que para el resto de las conexiones las resuelva de forma tradicional. “¿Pero que decis flaco? en GNU/Linux tambien me figura esa opción” Es cierto, en la configuración de VPN de GNU/Linux figura una opción similiar a la de Windows, pero al menos, en las distros que probe, no funciona. “¿y entonces que hago?” Para poder navegar por internet mientras estamos conectados a una VPN la solución es muy simple y conta de muy pocos pasos. Primero vamos a la configuración de VPN y nos dirigimos a la pestaña de IPv4 y ahi tildamos “Usar esta conexión sólo para los recursos en su red“. La teoria nos dice que con esa opción ya podriamos estar conectados a la VPN y aún asi no perder la conexión a itnernet, ya que se usaria la VPN “solo para los equipos pertenecientes a esa red”, sin embargo lo que sucedera al tildar esta opción es que permaneceremos conectados a la VPN, pero no tendremos acceso a ella, pero si a internet. Es decír, veremos que la VPN figura como conectada, podremos navegar por internet, pero no veremos los equipos de la VPN ni nos responderan el ping. Para solucionarlo, ejecutamos el comando ifconfig para ver que ip tenemos dentro de la VPN, y cual es la interface correspondiente para la misma: como vemos en la imagen, nuestra ip es 192.168.0.234 y nuestra interface es ppp0. Con estos datos ejecutamos el siguiente comando: sudo route add -net ipDeLaRedInternaDeLaVPN netmask mascaraDeLaRedInternaDeLaVPN gw nuestraIpEnLaVPN dev NuestraInterface Por ejemplo, en nuestro caso nuestra ip de la VPN, como ya digimos es 192.168.0.234, por lo tanto la ip de la red interna de la vpn sera 192.168.0.0 y la mascara de red de la red de la vpn sera 255.255.255.0, y el comando nos quedaria asi: sudo route add -net 192.168.0.0 netmask 255.255.255.0 gw 192.168.0.234 dev ppp0 De esta forma le estamos diciendo al sistema que todas las peticiones a las ip que comienzen con 192.168.0* salgan por la VPN. Si en lugar de toda la red quisieramos ver solo un equipo especifico dentro de la VPN simplemente ejecutamos el mismo comando reemplazando la ip de la red por la del equipo y a la mascara le agregamos un 255 al final. Por ejemplo, para ver solo el equipo 38, seria: sudo route add -net 192.168.0.38 netmask 255.255.255.255 gw 192.168.0.234 dev ppp0 Ahora ya estamos listos para poder seguir trabajando a través de una VPN y preguntarle a San Google como hacer el reporte que nos pidio el jefe.