16th marzo, 2009

BOX2D per iPhone: ovvero la fisica dei corpi grazie a BugMan !

by Stefano Lodu

Box2d è un simulatore di corpi rigidi in 2D sviluppato in C++. E’ molto utile perché permette in poche righe di creare un motore fisico e quindi al posto di crearsi tutte quelle routine per l’animazione degli oggetti lasciamo ad Dott. Isaac Newton di farlo per noi attraverso le regole della Fisica :)

Caratteristiche del motore fisico:

Collision

  • Continuous collision detection.
  • Contact callbacks: add, persist, remove.
  • Convex polyons and circles.
  • Multiple shapes per body
  • One-shot contact manifolds
  • Incremental sweep-and-prune broadphase
  • Efficient pair management
  • Fast broadphase AABB queries
  • Collision groups and categories

continua…

Physics

  • Continuous physics with time of impact island solver.
  • Persistent body-joint-contact graph
  • Island solution and sleep management
  • Contact, friction, and restitution
  • Stable stacking with a linear-time solver
  • Revolute, prismatic, distance, pulley, gear, and mouse joints
  • Joint limits, motors, and friction
  • Momentum decoupled position correction
  • Fairly accurate reaction forces/impulses

System

  • Small block and stack allocators
  • Centralized tuning parameters
  • Highly portable C++ with no use of STL containers

Testbed

  • OpenGL with Freeglut
  • Graphical user interface with GLUI
  • Easily switch between tests using GUI
  • Test framework for easily adding new tests
  • Mouse picking and the bomb!
  • VC8 project files

In particolare il TestBed ha anche un progetto per Xcode così da poter provare su Macosx
i vari esempi (sono tanti e tutti documentatissimi).

Dicevamo che è scritto in C++, in particolare la maggior parte delle funzioni e dei tipi iniziano con il prefisso b2.

E’ facilmente integrabile in qualunque progetto per Iphone, vediamo brevemente quali sono i passi da compiere per crearsi un progetto che abbia un motore Box2d al suo interno.

1) Scaricarsi il codice sorgente di Box2d da http://sourceforge.net/projects/box2d

Attualmente la versione è la 2.0.1  (13 Aprile 2008)

2) Scompattare il tutto su una cartella

3) Creare o aprirsi un progetto Xcode (magari uno che abbia una vista OpenGLEs)

4) Copiare (anche tramite drag & drop) la cartella Source dentro il nostro progetto Xcode

5) Nella propria view includere l’header di Box2d

#include “Box2D.h”

6) Rinominare il file della view in .mm (per mischiare C++ e objective C)

7) Inizialiare un mondo prima di procedere con l’attivazione della vista OpenGLES

Qualcosa di simile:

Da qualche parte definisco le variabili:

b2AABB worldAABB;
b2World *m_world;

E poi l’init del mondo è :

// inizializzo le coordinate del mondo
worldAABB.lowerBound.Set(-100.0f, -100.0f);
worldAABB.upperBound.Set(100.0f, 100.0f);

// Definisco il vettore di gravità
b2Vec2 gravity(0, -9.81f);

// Do we want to let bodies sleep?
bool doSleep = true;

// Costruisco il mondo
m_world= new b2World(worldAABB, gravity, doSleep);

// Definisco il corpo ground (terreno)
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f, -15.0f);

// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = m_world->CreateBody(&groundBodyDef);

// Define the ground box shape.
b2PolygonDef groundShapeDef;

// The extents are the half-widths of the box.
groundShapeDef.SetAsBox(50.0f, 10.0f);

// Add the ground shape to the ground body.
groundBody->CreateShape(&groundShapeDef);

8 ) Nel main loop (se vogliamo la DrawView della nostra vista OpenGLES) faccio avanzare
la fisica del mondo:

float32 timeStep = 1.0f / 60.0f;
int32 iterations = 10;

// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
m_world->Step(timeStep, iteration);

(timeStep può essere benissimo animationInterval che usiamo per la view)

9) a questo punto si possono creare oggetti e usando una draw adatta disegnarli per vedere cosa
succede, per esempio se mi creo un cerchio in questo modo:

posX e posY sono la posizione del cerchio
m_body è un oggetto (body) inizializzato così: b2Body *m_body;
m_world è il nostro mondo creato in precedenza
ang è l’angolo di rotazione (in radianti)

// Define the dynamic body. We set its position and call the body
b2BodyDef bodyDef;

bodyDef.position.Set(posX, posY);
bodyDef.angle = ang; // the body’s angle in radians.
//bodyDef.linearDamping = 0.0f;
//bodyDef.angularDamping = 0.01f;
//bodyDef.isBullet = false;
bodyDef.angularDamping = 0.02f;

m_body = m_world->CreateBody(&bodyDef);

// Define box shape for our dynamic body.
b2CircleDef circleDef;

// Set the box density to be non-zero, so it will be dynamic.
circleDef.density = 1.0f;

// Override the default friction.
circleDef.friction = 0.3f;

// Add the shape to the body.
m_body->CreateShape(&circleDef);

// Now tell the dynamic body to compute it’s mass properties base
// on its shape.
m_body->SetMassFromShapes();

Se vedete il codice al body va associato uno shape che sarà poi la forma che permette di controllarne la fisica, le collisioni e molto altro.
Se poi vogliamo disegnare questo oggetto procediamo in opengles a disegnare uno Sprite (c’e’ l’esempio dell’SDK che puo’ tornare utile) con la posizione corretta che prendiamo in questo modo:

// Vettore posizione del body:

b2Vec2 position = m_body->GetPosition();

// X sarà position.x e Y sarà position.y

// Angolo di rotazione del body

float32 angle = m_body->GetAngle();

Una volta presi questi parametri il disegno dell’oggetto è semplicemente tuto codice openGLES:

glPushMatrix();

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, spriteTexture);

if(m_isBlend) {
// Set a blending function to use
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// Enable blending
glEnable(GL_BLEND);
}

glTranslatef(position.x, position.y, 0.0f);
glRotatef(angle*(180/b2_pi), 0.0, 0.0, 1.0f);

glVertexPointer(2, GL_FLOAT, 0, m_vertices);
glEnableClientState(GL_VERTEX_ARRAY);

// va tolto il colore se è textured!!! SI MA LO FACCIO SU SETTEXTURE
glColor4f(m_color_r, m_color_g, m_color_b, 1.0);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

if(m_isBlend) {
glDisable(GL_BLEND);
}

if(m_isTextured) {
glDisable(GL_TEXTURE_2D);
}

glPopMatrix();

Per voi BugMan e un caloroso ringraziamento da Sir. Lodux !

Cresciamo insieme . Developer . Objective-C | Add your comment

One Comment. Subscribe to this post comments or trackback.

  1. Jasper
    giugno 23rd, 2009

    Grazie Mille per i consigli !!! scrivete qualcosa anche su chipmunk :)

Leave A Reply