Tutorial videojuegos: Físicas con Box2D

juego android super cauldron

Las físicas en los videojuegos son una opción muy interesante para hacer juegos más entretenidos. Programar a mano la física, con sus inercias, gravedad, etc es bastante complicado por ello lo normal es usar librerías que nos ayudan en esta tarea. Hoy explicaremos Box2D, una librería muy útil para la creación de físicas en nuestros videojuegos.

Como usar Box2D

Para poder usar Box2D deberemos incluir la libreria de Box2D de Andengine en nuestro proyecto. Para hacerlo deberemos descargar la libreria Box2d e incluirla de la forma habitual.

Creación del mundo en Box2D

Lo primero que tenemos que hacer en nuestro juego para que admita físicas es definir un mundo, el mundo es donde estarán todos los objetos, es como en la vida misma, el mundo sería el planeta tierra por ejemplo.

Al definir el mundo le deberemos indicar un vector que indicará la dirección de la gravedad y la "fuerza", normalmente el vector tendrá como coordenada X el 0 ya que normalmente la gravedad va en vertical.

// En andengine haremos esto en el createScene
// Creamos nuestro mundo, le pasamos un vector con la direccion de la gravedad y su fuerza. En este caso le ponemos que el empuje vaya hacia arriba con 9.8m/s2
mPhysicsWorld = new PhysicsWorld(new Vector2(0,  -9.8), true);

// Si estamos en andengine registramos en la escena el manegador de fisicas
mGameScene.registerUpdateHandler(this.mPhysicsWorld);  

Una vez que tengamos el mundo creado deberemos añadir los objetos al mundo.

// Definimos las categorias de máscaras de bits que habra en el juego. Esto se usará para saber si los objetos colisionan entre si o se atraviesan
public static final short CATEGORYBIT_MURO_NORMAL = 1;
public static final short CATEGORYBIT_MURO_BORDE = 2;
public static final short CATEGORYBIT_PLAYER_NORMAL =  4;

// El jugador chocara contra los muros y los objetos, por ello sumamos todas las mascaras
public static final short MASKBITS_PLAYER_NORMAL= CATEGORYBIT_MURO_NORMAL + CATEGORYBIT_MURO_BORDE + CATEGORYBIT_OBJETO+CATEGORYBIT_PLAYER_NORMAL; 
		
// Definimos las caracteristicas que tendra el objeto: densidad, elasticidad y friccion y le ponemos la mascara de bits del jugador
FixtureDef PLAYER_0_FIXTURE_DEF = PhysicsFactory.createFixtureDef(1f, 0.5f, 0.4f, false, CATEGORYBIT_PLAYER_NORMAL, MASKBITS_PLAYER_NORMAL, (short)0);

// Creamos el body, en nuestro caso será un cuerpo Circular, esto es importante para que el motor maneje bien los rebotes y rotaciones
// El metodo activity.getPhysicsWorld lo deberemos de crear en nuestra activity y debera devolver el mundo fisico que antes hemos creado
// En el BodyType le indicamos que es un cuerpo dinamico, es decir que se movera por el mundo. Si quisieramos hacer un muro o un objeto que no debe moverse deberemos poner BodyType.StaticBody
body = PhysicsFactory.createCircleBody(activity.getPhysicsWorld(), this, BodyType.DynamicBody,PLAYER_0_FIXTURE_DEF);

// Podemos ponerle datos al body (por si queremos tener informacion sobre su color, puntos, etc)
body.setUserData(LOQUESEA);

// Le decimos que puede rotar
body.setFixedRotation(false);

// Creamos un conector de fisicas para nuestro body		
physicsConnector =  new PhysicsConnector(this, body, true, true);		

// Registramos las fisicas en nuestro mundo
activity.getPhysicsWorld().registerPhysicsConnector(physicsConnector);

// Le registramos un contactlistener para manejar las colisiones. Mas adelante pondremos la clase UtilContactListener
activity.getPhysicsWorld().setContactListener(UtilContactListener.getContactListener(activity));

¿Como creamos objetos con otro tipo de interacción?

Hasta ahora hemos creado un objeto de cuerpo circular, podremos crear distintos tipos de cuerpos:


// Creamos una definicion para el cuerpo del objeto: Definimos su Densidad, elasticidad y friccion
FixtureDef OBJETO_FIXTURE_DEF = PhysicsFactory.createFixtureDef(1f, 0f, 1f, false);

// Cuerpo con forma cuadrada. De esta forma rebotara como un recuadro. Decimos que es un cuerpo dinamico, es decir que se moverá con fisicas en nuestro mundo
body = PhysicsFactory.createBoxBody(activity.getPhysicsWorld(), this, BodyType.DynamicBody, OBJETO_FIXTURE_DEF);

Mover objetos en el mundo físico

Mover objetos (sprites) en un mundo creado en box2d es algo distinto a hacerlo en la programación clásica de videojuegos. La forma de moverlo es aplicando fuerzas ya que un objeto si queremos que se mueva realisticamente no puede hacerse simplemente moviendolo por la pantalla. Por ejemplo, si queremos mover un objeto hacia la derecha hay que aplicarle una fuerza en esa dirección.

// APlicamos al body una fuerza con dos vectores, el primero es el vector de empuje y el segundo es el punto de impacto de la fuerza (normalmente el centro del objeto)
body.applyForce(new Vector2(x,-gravity), new Vector2(body.getWorldCenter()));

Manejo de colisiones e interacciones entre los objetos del mundo en BOX2D

// Esta clase se encargara de la gestion de las colisiones
public class UtilContactListener {
	public static ContactListener getContactListener(final ActivityGamePhysicsBasic activity){		
	    ContactListener contactListener = new ContactListener()    {
                // Se llama cuando comienza el contacto entre los objetos
	        public void beginContact(Contact contact){

	            // Cogemos los dos objetos que estan colisionando
	            final Fixture x1 = contact.getFixtureA();
	            final Fixture x2 = contact.getFixtureB();
                    // Ahora sacamos el valor del "Data" de cada objeto para gestionar que ocurrira al colisionar, por ejemplo si colisionas con una estrella la cogera, recordad que el valor del data lo deberemos haber metido al inicializar el objeto
	            if (userDataX1.getValue().equals("jugador") && x2.getBody().getUserData().equals("estrella")){	            			
	              	doLevelUp();
	            }	            
	        }
	        
	        public void endContact(Contact contact){
	        	
	            final Fixture x1 = contact.getFixtureA();
	            final Fixture x2 = contact.getFixtureB();
	            // Hacer lo que queramos hacer al finalizar el contacto
	        }

	        public void preSolve(Contact contact, Manifold oldManifold)
	        {
	        }

	        public void postSolve(Contact contact, ContactImpulse impulse)
	        {
	        }
	    };
	    return contactListener;
	}
}

Esperamos que con estros trocitos de código Android en Box2D os sea suficiente para comenzar en el mundo de las físicas

Ejemplo de juego con físicas Box2D

Os presenteo el juego que he hecho con físicas Box2D y que está disponible en Google Play. Magic Ball os agradecería que lo probárais y votárais con 5 estrellas. El juego es muy divertido.

Votos totales: 32