Armando un juego con fisica en Python, Pygame y Pymunk (I)

En El Chigüire Literario siempre he recomendado a aquellos que están haciendo un juego por primera vez en su vida que desarrollen un juego en 2D. La reacción suele ser adversa: desarrollar un juego en 2D es como volver al pasado, en una época en la que existen los juegos 3D y han adquirido un realismo notable. Supongo que a esta percepción le debemos sumar la visión del juego 2D como el juego vieja escuela (pensemos en MegaMan) donde la sensación de juego es como la de un arcade, en contraste con los juegos en 3D ricos en interacciones físicas que se asemejan a la realidad.

La buena noticia es que esto no se limita solamente al 3D. Se han hecho excelentes librerías para tener simulaciones físicas para el caso 2D que se sientan reales. Una de estas librerías es la ya famosa Box2D, por Eric Catto, utilizada principalmente en el juego Angry Brids. Otra librería muy completa, que es la que reseñaremos en este post, es Chipmunk. Chipmunk es una librería de física de cuerpos rígidos en 2D escrita en C. Para algunos universitarios la mención de “física de cuerpos rígidos” puede causarles dolor en su cuerpo (valga el mal juego de palabras 😛 ), pero para fines de este tutorial, lo único que tenemos que comprender son los conceptos que introduce la librería y cómo se deben utilizar para nuestros fines. Al estar escrita la librería en C, significa que hay una gran diversidad de bindings a otros lenguajes de programación, por lo que podemos usar esta librería escribiendo código en el lenguaje de nuestra preferencia.

Como en este sitio nos gusta Python y PyGame, aprovecharemos Pymunk, el binding para Python que tiene Chipmunk. Es tan fácil de instalar como PyGame, por lo que en pocos instantes vas a tener el poder de crear juegos con interacciones físicas realísticas y sin tener que aprender un montón de conceptos antes de comenzar.

¿Qué conceptos?

Para utilizar Pymunk hay que comprender unos conceptos propios de un simulador de cuerpos rigidos. Los 4 principales son:

Space (Espacio) – Todos los fenómenos ocurren dentro de un espacio, que no es más que un sistema de coordenadas. Es tarea del desarrollador que el sistema de coordenadas del espacio corresponda al de la pantalla. Cuando hablemos de Pygame lo explicaremos mejor.

Body (Cuerpo) – Todos los cuerpos dentro de un espacio tienen propiedades físicas. En el caso de 2 dimensiones, tenemos la masa y tenemos la inercia. La masa es la cantidad de materia en el cuerpo, y para Pymunk indica qué tanta fuerza hay que aplicar en centro de gravedad para moverlo. Si han visto alguna materia sobre física de cuerpos unidimensionales, reconocerán que esto es lo mismo. Por otro lado, la inercia representa la cantidad de fuerza que hay que aplicar en el cuerpo para que éste rote sobre su centro de gravedad.

Un cuerpo con muchísima masa será casi inamovible por cualquier fuerza, y un cuerpo con muchísima inercia será muy difícil de hacer girar. Son dos propiedades distintas: un cuerpo puede tener una masa tan grande que no se pueda mover y aún así tener muy poca inercia para girarse (ver el caso de una barra atravesada por un clavo de tal manera que un punto está fijo en la pared pero la barra puede girar libremente).

No hemos hablado de la forma que puede tener el cuerpo. Los cuerpos en Pymunk no tienen esta información, son entes que no pueden existir en el espacio si no vienen acompañados de una forma.

Shape (Forma) – La forma es la figura que adopta el cuerpo. Esta figura puede ser un círculo, un rectángulo o un polígono convexo. En todos los casos la forma tiene un “offset” que es el centro de gravedad del cuerpo. Como la inercia de un cuerpo suele venir dada por la figura, Pymunk tiene métodos para hacer un cálculo aproximado de esa inercia.

Joint (Junta) – Los joints proveen restricciones al movimiento de los cuerpos. Un caso que ya mencionamos es el de la barra fija a la pared mediante un clavo. Otro caso puede ser limitar el ángulo de movimiento de esa barra. Otro caso puede ser conectar dos engranajes. Existe una diversidad de juntas para múltiples necesidades.

¿Cómo nos iniciamos?

Para comenzar, asumiendo que tenemos instalado Python y Pygame, hay que instalar Pymunk.  En Windows es tan sencillo como descargar el instalador de la página de descargas, y en Linux se descarga la fuente y se instala con el procedimiento de ejecutar “python setup.py install” en el directorio descomprimido del proyecto. Los scripts que veremos a continuación están basados en el tutorial SlideAndPinJointsExample de la página de Pymunk.

Una vez hecho eso, podemos escribir un pequeño script que nos haga probar que pymunk se puede importar bien.

import sys
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk # Importamos Pymunk
def main():
	pygame.init()
	screen = pygame.display.set_mode((600, 600))
	pygame.display.set_caption("Pymunk test")
	clock = pygame.time.Clock()
	running = True
	pymunk.init_pymunk() # Inicializamos Pymunk
	space = pymunk.Space() # Creamos el Space
	space.gravity = (0.0, -900.0) # Colocamos gravedad: una fuerza constante en el tiempo
	while running:
		for event in pygame.event.get():
			if event.type == QUIT:
				running = False
			elif event.type == KEYDOWN and event.key == K_ESCAPE:
				running = False
		screen.fill(THECOLORS["white"])
		space.step(1/50.0) # Hacemos que la simulación corra 1/50.0 de cada segundo
		pygame.display.flip()
		clock.tick(50) # Como este loop corre 50 veces por segundo, estimamos que este código se comportará aproximadamente en tiempo real.
if __name__ == '__main__': # __name__ solo tiene el valor '__main__' cuando este script se corre directamente y no se importa desde otro lado.
	sys.exit(main())

¿Qué relación tiene con PyGame?

Pymunk ofrece una simulación bastante precisa (para nuestros fines) del movimiento de cuerpos rígidos, pero no tiene forma de mostrar eso en pantalla. PyGame en este caso sería el encargado de: 1) hacer la representación en pantalla de la simulación (algo tan sencillo como líneas y puntos, o algo más complicado con sprites), 2) realizar alguna tarea relacionada con el producto de las colisiones entre elementos de Pymunk, como producir sonido o lanzar algún evento, 3) establecer el timer para ejecutar la simulación paso por paso en el tiempo (con el objeto Clock).

Expandiremos el ejemplo anterior colocando un generador de esferas en el loop y dibujándolo con las funciones de Pygame:

import sys
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk
def add_ball(space): # Esta funcion agrega una esfera
    mass = 1
    radius = 14
    inertia = pymunk.moment_for_circle(mass, 0, radius) # Por lo general la inercia se saca por la forma aproximada del cuerpo
    body = pymunk.Body(mass, inertia) # Creamos el cuerpo
    x = random.randint(120,380)
    body.position = x, 550 # Le asignamos una posicion al azar en el eje horizontal
    shape = pymunk.Circle(body, radius) # Al cuerpo le asignamos una forma
    space.add(body, shape) # Tnato el cuerpo como su forma se agregan al espacio
    return shape
def draw_ball(screen, ball): # Esta funcion dibuja la esfera en pantalla
    p = int(ball.body.position.x), 600-int(ball.body.position.y)
    pygame.draw.circle(screen, THECOLORS["blue"], p, int(ball.radius), 2)
def main():
	pygame.init()
	screen = pygame.display.set_mode((600, 600))
	pygame.display.set_caption("Pymunk test")
	clock = pygame.time.Clock()
	running = True
	pymunk.init_pymunk() # Inicializamos Pymunk
	space = pymunk.Space() # Creamos el Space
	space.gravity = (0.0, -900.0) # Colocamos gravedad: una fuerza constante en el tiempo
		balls = []
	ticks_to_next_ball = 10
	while running:
		for event in pygame.event.get():
			if event.type == QUIT:
				running = False
			elif event.type == KEYDOWN and event.key == K_ESCAPE:
				running = False
		ticks_to_next_ball -= 1
		if ticks_to_next_ball <= 0:
			ticks_to_next_ball = 25
			ball_shape = add_ball(space)
			balls.append(ball_shape)
		screen.fill(THECOLORS["white"])
		for ball in balls:
			draw_ball(screen, ball)
		space.step(1/50.0)
		pygame.display.flip()
		clock.tick(50)
if __name__ == '__main__': # __name__ solo tiene el valor '__main__' cuando este script se corre directamente y no se importa desde otro lado.
	sys.exit(main())

Y en el proximo capitulo...

Hasta aca llegamos con el articulo de hoy sobre Pymunk. Los scripts que aparecen en este articulo se pueden descargar aqui. En el siguiente articulo veremos como utilizar los sprites de Pygame para tener una representacion mas fiel a la realidad de nuestras simulaciones, y veremos que debemos emplear en Pymunk para hacer un pequeno juego de plataformas. Por ahora los dejo con lo que haremos en el siguiente articulo.

Asteroids con fisica

24 comentarios sobre “Armando un juego con fisica en Python, Pygame y Pymunk (I)”

  1. Acabo de cursar una materia de inteligencia artificial para videojuegos. El proyecto ( @FatBoyGame ) lo desarrollé completamente en python con pygame. Ciertamente esta combinación fue perfecta y me ayudó a avanzar rapidísimo en el desarrollo del juego. Me hubiese gustado contar adicionalmente con un motor de física.

    ¿Lo mejor de python? programar funcionalmente como si fuese Haskell. No hay nada como utilizar lambda, reduce, map, etc.

    ¿Lo mejor de pygame? pygame.sprite.Group() te permiten crear grupos de sprites. Si cada ente en tu juego es un sprite y tiene asociados grupos tales como gravitacional, colisiones, inteligencia. Puedes aplicar cambios a cualquier elemento en uno de estos grupos sin la necesidad de que tu estés agregando y eliminando a estos objetos del grupo, se hace todo solo 😀

  2. Hola, me acabo de dar cuenta mientras probaba jedi chicken que el método init_pymunk() lo eliminaron de las versiones nuevas (para ser más específico en las versiones 2.algo no funcionará), por lo que para seguir estos códigos es importante instalar la 1.0.0. Saludos.

  3. ayuda descarge el pyton 2.7 y el 3.2.2 y sus pymunk y pygame respectivas y los trato de instalar (una de las versiones a la vez) y me dice que pyton x.x.x es requerido que podra ser que puedo hacer

    1. Te recomendaría que trabajaras con Python 2.7 ya que Pygame todavía no tiene versión correspondiente con las versiones 3.X. Y trabaja con una sola versión de Python porque eso puede confundir a los instaladores en Windows.

Deja un comentario