Intro Box2D

Giới thiệu
Box2D là cái tên mà mọi người biết đến là engine vật lý 2D tốt nhất hiện nay, điều này có thể hơi khó tưởng tượng khi có rất nhiều engine được đặt tên theo tên của các loài động vật, nhưng cái tên cũng chỉ là cái tên. Engine này khá nhẹ, hiệu quả, mạnh và tính tương thích cao, miễn phí và là mã nguồn mở. Website chính thức của Box2D là http://www.box2d.org .
Một engine vật lý mô phỏng môi trường vật lý của các object sao cho giống thật nhất. Mặc dù nó có thể được sử dụng cho các ứng dụng khác, Box2D được tạo ra chủ yếu làm thư viện sử dụng trong game. Engine này được viết và bảo trì bởi Erin Catto khi một ngày ông không hài lòng về engine vật lý mà ông đang làm việc.
Box2D được viết bằng C++, nhưng được chuyển sang nhiều ngôn ngữ khác bởi cộng đồng người sử dụng. Dưới đây là một số loại của Box2D, chúng ta có thể tìm hiểu thêm tại http://www.box2d.org/links.html :

  • (Flash) http://www.box2dflash.org/
  • (Flash) http://www.sideroller.com/wck/
  • (Java) http://www.jbox2d.org/
  • (Python) http://code.google.com/p/pybox2d/
  • (Javascript) http://box2d-js.sourceforge.net/
  • (C#) http://code.google.com/p/box2dx/
    Engine vật lý không phải là một game engine. Box2D rất mạnh trong việc mô phỏng vật lý nhưng nó không được thiết kế để tận dụng sức mạnh đồ họa, network, xây dựng game hoặc load game level. Để đáp ứng những nhu cầu này, chúng ta có thể xem một trong những link dưới đây :
  • cocos2d iPhone : http://code.google.com/p/cocos2d-iphone/
  • Citrus engine : http://citrusengine.com/
  • Corona SDK : http://www.anscamobile.com/corona/
  • Game salad : http://gamesalad.com/
  • Pixelwave (iOS) : http://www.pixelwave.org/docs/Introduction_to_Pixelwave
  • Dragonfire SDK : http://www.dragonfiresdk.com/
    BODIES
    Bodies là một đối tượng cơ sở trong lớp vật lý, nhưng chúng không phải là thứ chúng ta có thể thấy chúng chạy vòng quanh và va chạm với các đối tượng khác.
    Chúng ta có thể xem body là các thuộc tính của một đối tượng mà chúng ta không thể thấy (draw) hoặc va chạm được (collide with). Những thuộc tính ẩn này gồm :
  • Mass : khối lượng
  • Velocity : vận tốc và hướng di chuyển
  • Rotational inertia : quán tính quay
  • Angular velocity : vận tốc tiếp tuyến và độ lớn của vận tốc
  • Location : vị tri
  • Angle : hướng của vật thể
    Mặc dù chúng ta đều biết tất cả đặc tính của một đối tượng, chúng ta vẫn không biết được nó như thế nào và cách mà nó xảy ra khi va chạm với các đối tượng khác. Để thiết lập kích thước và hình dạng của một đối tượng chúng ta sử dụng fixtures, chúng ta sẽ bàn đến fixtures ở phần sau.
    Có 3 loại body : static (tĩnh), dynamic (động) và kinematic (động học). Hai loại đầu khá dễ hiểu khi đọc tên trong khi loại cuối cùng khá khó hiểu. Trước hết chúng ta thử tạo một đối tượng sử dụng dynamic body.
    Tạo một body
    Đầu tiên body được định nghĩa, sau đó sử dụng nó để tạo một body object.Điều này khá tiện khi chúng ta muốn tạo ra nhiều body giống nhau hoặc tương đương nhau. Dưới đây là code khởi tạo một body :
b2BodyDef myBodyDef;
myBodyDef.type = b2_dynamicBody; //this will be a dynamic body
myBodyDef.position.Set(0, 20); //set the starting position
myBodyDef.angle = 0; //set the starting angle

Lưu ý là body không có kích thước, hình dạng, vì vậy chúng ta không định nghĩa chúng ở đây. Có thể bạn sẽ tự hỏi tại sao không định nghĩa thuộc tính Mass – thong thường chúng ta thường định nghĩa thuộc tính mass cho một body bằng cách thêm một fixture vào body đó (sẽ được mô tả ở phần sau). Bây giờ chúng ta sẽ định nghĩa một thể hiện body :

b2Body* dynamicBody = m_world->CreateBody(&mybodydef);

Ở đây chúng ta sử dụng biến m_world là một đối tượng b2World. Đối tượng world giống như quản lý toàn bộ mọi thứ trong Box2D, nó xử lý việc tạo và xóa các đối tượng vật lý. Chúng ta sẽ xem xét các thuộc tính của B2Word sau. Bây giờ chúng ta đã có 1 body, nhưng một body cơ bản vẫn là một đối tượng cơ sở như đã nói ở phần đầu, vì vậy nếu chúng ta build và run chương trình chúng ta sẽ không thấy gì cả.
Để thiết lập kích thước, hình dạng cho một body chúng ta add fixture vào body đó. Ngoài ra, chúng sẽ mặc định ảnh hưởng thuộc tính mass của body. Một body có thể có nhiều fixture được add vào, mỗi fixture được add vào sẽ gây ảnh hưởng đến toàn bộ mass của body đó. Bây giờ chúng ta sẽ add một fixture đơn giản vào body mà chúng ta vừa tạo, một hình vuông :

b2PolygonShape boxShape;
boxShape.SetAsBox(1,1);
b2FixtureDef boxFixtureDef;
boxFixtureDef.shape = &boxShape;
boxFixtureDef.density = 1;
dynamicBody->CreateFixture(&boxFixtureDef);

Mặc dù hiện tại chúng ta có thể bỏ qua phần lớn code ở trên, tuy nhiên chúng ta cần lưu ý đến thuộc tính density. Thuộc tính mass được tính bằng cách lấy diện tích của fixture nhân với density của chúng. (density : mức độ dày đặc của một vật)
Bây giờ khi chạy chương trình, chúng ta sẽ thấy một hình vuông nhỏ rơi xuống dưới, chúng ta có thể sử dụng chuột để click vào nó và ném chúng xung quanh, nhấn phím R nếu nó rơi mất khỏi màn hình. Body dynamic có thể di chuyển, xoay và bị tương tác bởi trọng lực.

Thiết lập các thuộc tính của body
Bây giờ chúng ta sẽ thêm một số thuộc tính đã được nhắc ở phần đầu. Ví dụ chúng ta sẽ thay đổi vị trí và góc :

dynamicBody->SetTransform( b2Vec2( 10, 20 ), 1 );

Lúc này body sẽ ở vị trí (10, 20) theo trục tọa độ OpenGL (Gốc tọa độ ở góc dưới bên trái) và xoay 1 radian ngược chiều kim đồng hồ. Box2D sử dụng radian để đo độ lớn của góc, vì vậy nếu muốn chuyển từ radian sang độ và ngược lại chúng ta có thể dùng như sau :

#define DEGTORAD 0.0174532925199432957f
#define RADTODEG 57.295779513082320876f
dynamicBody->SetTransform( b2Vec2( 10, 20 ), 45 * DEGTORAD );  //45 degrees counter-clockwise

Chúng ta cũng có thể thiết lập vận tốc tịnh tiến và vận tốc tiếp tuyến của body :

dynamicBody->SetLinearVelocity( b2Vec2( -5, 5 ) ); 	//moving up and left 5 units per second
dynamicBody->SetAngularVelocity( -90 * DEGTORAD ); //90 degrees per second clockwise

Static bodies
Bây giờ chúng ta sẽ xem tác dụng của static body. Bởi vì chúng ta đã định nghĩa một body và một fixture, chúng ta có thể sử dụng lại chúng và thay đổi một số đặc tính cần thiết :

myBodyDef.type = b2_staticBody;	 //this will be a static body
myBodyDef.position.Set(0, 10); 	//slightly lower position
b2Body* staticBody = m_world->CreateBody(&myBodyDef); 		//add body to world
staticBody->CreateFixture(&boxFixtureDef); 	//add fixture to body

Lưu ý rằng chúng ta không cần phải thay đổi tất cả thuộc tính của fixture. Chạy chương trình và chúng ta sẽ thấy một hình vuông khác trên màn hình, nhưng làn này nó không di chuyển. Ngoài ra, chúng ta có thể setTransform để thay đổi vị trí cho static body nhưng không thể thiết lập vận tốc cho static body được, nó sẽ không có tác dụng.

Kinematic bodies
Cuối cùng, chúng ta sẽ xem kinematic body là gì. Như chúng ta thấy, dynamic bodies di chuyển và static bodies đứng yên. Khi một static body va chạm một dynamic body, static body luôn luôn giữ nguyên vị trí, và dynamic body sẽ bị phản lại vì vậy chúng không thể chồng lên nhau được. Kinematic body cũng khá giống với static body ở điểm khi chúng va chạm với một dynamic body nó luôn luôn giữ vị trí của nó và dynamic body bị dội ngược lại. Điểm khác biệt là kinematic body có thể di chuyển.
Chúng ta có thể thiết lập một kinematic body như sau :

myBodyDef.type = b2_kinematicBody; 	//this will be a kinematic body
myBodyDef.position.Set(-18, 11); 	// start from left side, slightly above the static body
b2Body* kinematicBody = m_world->CreateBody(&myBodyDef);	 //add body to world
kinematicBody->CreateFixture(&boxFixtureDef); 		//add fixture to body
kinematicBody->SetLinearVelocity( b2Vec2( 1, 0 ) );		 //move right 1 unit per second
kinematicBody->SetAngularVelocity( 360 * DEGTORAD ); 	//1 turn per second counter-clockwise


Body trên màn hình có thể di chuyển và xoay, nhưng nó không chịu tác dụng của trọng lực, và không bị ảnh hưởng khi va chạm với một dynamic body. Lưu ý rằng khi nó va chạm với một static body, không có sự tương tác giữa chúng.
Trong hầu hết các game, dynamic body được sử dụng cho player và các nhân vật khác trong game, static body được sử dụng cho tường, nền đất và các thứ khác. Kinematic body được sử dụng cho những body có thể di chuyển và xoay nhưng không bị ảnh hưởng khi va chạm với dynamic body.
To be continue…

Lấy các thuộc tính của body
Thông thường chúng ta muốn biết vị trí của một body hoặc vận tốc của chúng, góc xoay … Để làm điều này chúng ta cần truy cập vào biến body, vì vậy chúng ta cần khai báo biến body thuộc class thay vì là biến cục bộ như trước :

//in the class itself, not inside the constructor!
b2Body* dynamicBody;

… và thay đổi phần khai báo lúc body được khởi tạo. Bây giờ bên trong hàm Step(), chúng ta có thể in ra một số thông tin của một body :

b2Vec2 pos = dynamicBody->GetPosition();
float angle = dynamicBody->GetAngle();
b2Vec2 vel = dynamicBody->GetLinearVelocity();
float angularVel = dynamicBody->GetAngularVelocity();
m_debugDraw.DrawString(5, m_textLine,
"Position:%.3f,%.3f Angle:%.3f", pos.x, pos.y, angle * RADTODEG);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine,
"Velocity:%.3f,%.3f Angular velocity:%.3f", vel.x, vel.y, angularVel * RADTODEG);
m_textLine += 15;


Chúng ta có thể truyền GetPosition() và GetAngle() vào hàm SetTransform nhưng sẽ không có gì thay đổi. Ví dụ :

body->SetTransform( body->GetPosition(), body->GetAngle() );

Dĩ nhiên nó không có tác dụng gì, nhưng nếu chúng ta chỉ muốn thay đổi vị trí hoặc góc, chúng ta có thể thực hiện theo cách này.
Tương tác với toàn bộ các bodies trong world
Nếu chúng ta muốn tương tác với toàn bộ các bodies trong world, chúng ta có thể làm như bên dưới. Phương thức GetBodyList() trả về ơphần tử đầu tiên trong danh sách liên kết các bodies :

for ( b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
//do something with the body 'b'
}

Dọn rác
Khi chúng ta không sử dụng một body, chúng ta có thể xóa chúng bằng cách gọi phương thức DestroyBody của đối tượng world :

m_world->DestroyBody(dynamicBody);

Khi một body được hủy như thế này, nó sẽ chịu trách nhiệm xóa toàn bộ các fixture của body này. Nên nhớ rằng không được delete con trỏ body sau khi gọi hàm này.
FIXTURES
Fixture được dùng để mô tả kích thước, hình dạng và đặc điểm vật chất của một đối tượng trong vật lý. Một body có thể có nhiều fixture được gắn vào chúng, và trọng tâm của body sẽ bị ảnh hưởng bởi những fixture này. Khi 2 body va chạm với nhau, fixture của chúng được sử dụng để quyết định kết quả của sự va chạm. Những thuộc tính chính của fixture :

  • Shape : hình dạng
  • Restitution : tính đàn hồi
  • Friction : độ trượt (ma sát)
  • Density : độ nặng
    Chúng ta sẽ đi qua từng thuộc tính ở trên và xem thử chúng có ảnh hưởng như thế nào đến đối tượng. Chúng ta sẽ dùng một dynamic body mà đã được tạo ở phần bodies, ta sẽ có constructor của lớp FooTest như sau :
FooTest() {
b2BodyDef myBodyDef;
myBodyDef.type = b2_dynamicBody; //this will be a dynamic body
myBodyDef.position.Set(-10, 20); //a little to the left
b2Body* dynamicBody1 = m_world->CreateBody(&myBodyDef);
}

SHAPES
Mỗi fixture đều có một hình dạng được dùng để xét va chạm với các fixture khác. Một fixture có thể là hình tròn(circle) hoặc đa giác(polygon). Chúng ta hãy thử tạo một shape circle…

b2CircleShape circleShape;
circleShape.m_p.Set(0, 0); //position, relative to body position
circleShape.m_radius = 1; //radius
… và một fixture sử dụng shape này :
b2FixtureDef myFixtureDef;
myFixtureDef.shape = &circleShape; //this is a pointer to the shape above
dynamicBody1->CreateFixture(&myFixtureDef); //add a fixture to the body

Chạy chương trình chúng ta sẽ thấy một hình tròn trên màn hình :

Khi thiết lập vị trí của một circle, trục tọa độ sẽ liên quan đến vị trí của body. Trong trường hợp chúng ta thiết lập vị trí của hình tròn này là (0, 0) nhưng nếu circle này được đính vào body có tọa độ là (0, 20) thì circle còn cũng có vị trí là (0, 20).
Bây giờ chúng ta sẽ thử tạo một shape polygon. Chúng ta sẽ thiết lập từng đỉnh (vertex) của polygon để có hình dạng như chúng ta mong muốn. Ở đây chúng ta sẽ tạo một polygon với 5 đỉnh :

//set each vertex of polygon in an array
b2Vec2 vertices[5];
vertices[0].Set(-1,  2);
vertices[1].Set(-1,  0);
vertices[2].Set( 0, -3);
vertices[3].Set( 1,  0);
vertices[4].Set( 1,  1);
b2PolygonShape polygonShape;
polygonShape.Set(vertices, 5); //pass array to the shape
myFixtureDef.shape = &polygonShape; //change the shape of the fixture
myBodyDef.position.Set(0, 20); //in the middle
b2Body* dynamicBody2 = m_world->CreateBody(&myBodyDef);
dynamicBody2->CreateFixture(&myFixtureDef); //add a fixture to the body


Một số lưu ý khi khởi tạo một polygon theo cách trên. Đầu tiên là mỗi polygon được tạo tối đa 8 đỉnh. Nếu chúng ta muốn tạo nhiều đỉnh hơn, chúng ta có thể thiết lập lại giá trị b2_maxPolygonVertices trong file b2Setting.h. Các vertex phải được khai báo theo thứ tự ngược chiều kim đồng hồ, và chúng phải là đa giác lồi.
Nếu chúng ta muốn một fixture hình chữ nhật, cách dễ nhất để tạo một hình chữ nhất là sử dụng phương thức SetAsBox :

polygonShape.SetAsBox(2, 1); //a 4x2 rectangle
myBodyDef.position.Set(10,20); //a bit to the right
b2Body* dynamicBody3 = m_world->CreateBody(&myBodyDef);
dynamicBody3->CreateFixture(&myFixtureDef); //add a fixture to the body


Lưu ý rằng các đối số của SetAsBox là nữa chiều rộng và nữa chiều cao của hình chữ nhật, và trọng tâm của nó được đặt ở tọa độ của body mà nó được đính vào. Fixture mà chúng ta định nghĩa giữ một con trỏ tham chiếu đến polygon shape mà chúng ta đã tạo lần trước, và chúng ta chỉ thay đổi shape của chúng, vì vậy chúng ta không cần thiết lập các thuộc tính khác.
Đôi khi chúng ta muốn một fixture biểu diễn một đoạn thẳng với độ rộng dày bằng 0. Chúng ta sử dụng phương thức SetAsEdge để làm điều này, phương thức này sử dụng 2 điểm là 2 đầu của đoạn thẳng. Chúng ta sẽ tạo một static body là một fixture đường thằng, nằm gần đáy của màn hình vì vậy các dynamic object có thể rơi xuống nó.

myBodyDef.type = b2_staticBody; //change body type
myBodyDef.position.Set(0,0); //middle, bottom
polygonShape.SetAsEdge( b2Vec2(-15,0), b2Vec2(15,0) ); //ends of the line
b2Body* staticBody = m_world->CreateBody(&myBodyDef);
staticBody->CreateFixture(&myFixtureDef); //add a fixture to the body


Lưu ý : Phương thưc SetAsEdge cho polygon đã được loại bỏ từ Box2D version 2.1.2 trở lên bởi vì chúng có một kiểu shape riêng, b2EdgeShape.
To be continue…

DENSITY
Có thể chúng ta sẽ thắc mắc tại sao một body không xoay xung quanh khi chúng ta dùng chuột click vào body và kéo xung quanh. Đó là bởi vì chúng ta chưa thiết lập thuộc tính density cho fixture. Density của một fixture nhân cho diện tích sẽ bằng với khối lượng của fixture đó. Lúc khởi tạo fixture, chúng thêm thuộc tính density vào đó :

b2FixtureDef myFixtureDef;
...
myFixtureDef.density = 1; //new code

Khi tất cả các body sử dụng chung một fixture thì nó sẽ ảnh hưởng đến toàn bộ các body đó.

Trong ví dụ này, tất cả các fixture có cùng một sensity, nghĩa là chúng kích thước của fixture càng lớn thì khối lượng của chúng cũng càng lớn, vì vậy hình tròn sẽ nhẹ nhất.
Multiple fixture
Chúng ta có thể đính 3 loại fixture này lên một body, và không khó để làm việc này. Chúng ta đã sử dụng hàm CreateFixture để khởi tạo dynamicBody1, dynamicBody2, dynamicBody3, thay vì làm vậy chúng ta sẽ add tất cả các fixture vào dynamicBody1.

Như chúng ta thấy vị trí của các fixture phụ thuộc vào vị trí của dynamicBody1. Khá dễ thấy rằng các fixture nằm chồng lên nhau, tuy nhiên chúng ta có thể đính một fixture vào một body ở bất kì đâu, thậm chí tạo ra khoảng cách giữa chúng. Để làm được việc này chúng ta cần thiết lập lại vị trí của fixture khi chúng ta khởi tạo chúng.

//for the custom polygon, add 10 to each x-coord
vertices[0].Set(-1 +10,  2);
vertices[1].Set(-1 +10,  0);
vertices[2].Set( 0 +10, -3);
vertices[3].Set( 1 +10,  0);
vertices[4].Set( 1 +10,  1);
...
//for the box, use an extended version of the SetAsBox function which allows
//us to set a location and angle (location is offset from body position)
polygonShape.SetAsBox(2, 1, b2Vec2(20,0), 0 ); //moved 20 units right, same angle

Kết quả chúng ta sẽ nhận được như hình bên dưới :

FRICTION
Chúng ta đã biết cách thiết lập một body với nhiều fixture, lần này chúng ta sẽ thiết lập một body với 4 fixture hình vuông :

FooTest() {
//set up a dynamic body
b2BodyDef myBodyDef;
myBodyDef.type = b2_dynamicBody;
myBodyDef.position.Set(0, 20); //middle
b2Body* dynamicBody = m_world->CreateBody(&myBodyDef);
//prepare a shape definition
b2PolygonShape polygonShape;
b2FixtureDef myFixtureDef;
myFixtureDef.shape = &polygonShape;
myFixtureDef.density = 1;
//add four square shaped fixtures around the body center
for ( int i = 0; i < 4; i++) {
b2Vec2 pos( sinf(i*90*DEGTORAD), cosf(i*90*DEGTORAD) ); //radial placement
polygonShape.SetAsBox(1, 1, pos, 0 ); //a 2x2 rectangle
dynamicBody->CreateFixture(&myFixtureDef); //add a fixture to the body
}
//make a static floor to drop things on
myBodyDef.type = b2_staticBody;
myBodyDef.position.Set(0, 0); //middle, bottom
b2Body* staticBody = m_world->CreateBody(&myBodyDef);
polygonShape.SetAsEdge( b2Vec2(-15,0), b2Vec2(15,3) ); //slightly sloped
staticBody->CreateFixture(&myFixtureDef); //add a fixture to the body
}


Lần này chúng ta sẽ thấy body trượt trên dốc mà chúng ta tạo ra. Khi mỗi fixture chạm vào mặt bên dưới, friction được dùng để thiết lập mức độ trượt (ma sát) của body. Giá trị của friction nằm trong khoảng [0 – 1], 0 là hoàn toàn không có ma sát. Khi 2 fixture va chạm với nhau, giá trị friction tổng hợp có xu hướng thấp hơn giá trị của chúng.
Thiết lập một fixture với zero friction :

myFixtureDef.density = 1;
myFixtureDef.friction = 0; //new code

Lần này body xoay ít hơn khi các mặt của body hoàn toàn không dính trên dốc đoạn thẳng, và nó sẽ tăng tốc dần. Bây giờ chúng ta sẽ thiết lập friction là 1 để so sánh kết quả.
Lưu ý : Giá trị của friction là 1 không đồng nghĩa với việc vật đó sẽ không trượt.
Restitution
Restitution định nghĩa độ đàn hồi của một fixture. Giống friction, giá trị của restitution nằm trong khoảng [0-1], khi giá trị 0 nghĩa là fixture sẽ hoàn toàn không đàn hồi, và 1 nghĩa mà toàn bộ lực mà body tác dụng sẽ tác dụng ngược lại hoàn toàn. Khi 2 fixture va chạm với nhau, restitution tổng có xu hướng cao hơn giá trị restitution của chúng.
Giá trị restitution có thể được thiết lập giống như friction :

myFixtureDef.friction = ...;
myFixtureDef.restitution = 0; //new code

Lưu ý : Giá trị restitution bằng 0 không đồng nghĩa với việc nó hoàn toàn không đàn hồi
Lưu ý : Trong thực tế, một lượng nhỏ năng lượng sẽ bị thất thoát trong quá trình tính toán đàn hồi
Thay đổi thuộc tính fixture lúc runtime
Đôi khi chúng ta muốn thay đổi các thuộc tính của fixture dựa trên các sự kiện trong game. Chúng ta có thể thay đổi friction, density và restitution bằng cách thiết lập chúng sử dụng phương thức set. Nếu chúng ta có một tham chiếu đến fixture cần thay đổi, chúng ta sẽ làm như sau :

fixture->SetDensity( ... );
fixture->SetRestitution( ... );
fixture->SetFriction( ... );

Nếu chúng ta chỉ có tham chiếu đến body, chúng ta cần phải sử dụng iterator để tìm fixture mà chúng ta muốn tìm.
*Thiết lập density là một trường hợp đặc biệt khi cần thêm một bước nữa để thay đổi có tác dụng. Sau khi SetDensity() của fixture, chúng ta cần gọi phương thức ResetMassData() trên body có chứa fixture đó.
Duyệt các fixture trên một body
Nếu chúng ta có một body và chúng ta muốn duyệt tất cả các fixture của body đó, chúng ta có thể làm như bên dưới. Phương thức GetFixtureList() trả về phần tử đầu tiên trong danh sách liên kết của fixture.

for (b2Fixture* f = body->GetFixtureList(); f; f = f->GetNext())
{
//do something with the fixture 'f'
}

Nếu chúng ta chỉ muốn lấy một fixture trên body, chúng ta đơn giản lấy phần tử đầu tiên trong danh sách :

b2Fixture* f = body->GetFixtureList();
//do something with the fixture 'f'

Dọn dẹp
Nếu chúng ta muốn hủy một fixture ra khỏi một body, gọi phương thức DestroyFixture :

b2Fixture* myFixture = dynamicBody->CreateFixture(&myFixtureDef);
...
dynamicBody->DestroyFixture(myFixture);

Lưu ý là không delete con trỏ fixture sau khi gọi hàm này. Khi chúng ta hủy một body, tất cả các fixture được đính cũng sẽ bị hủy theo, vì vậy trong trường hợp này, chúng ta không thể dùng fixture sau khi hủy nó.

WORLD
World đã được nhắc đến trong các phần trước như là một thực thể chính mà ở đó chứa toàn bộ các Box2D body. Khi chúng ta tạo hoặc hủy một body, chúng ta gọi một phương thức từ đối tượng world để làm điều này, vì vậy đối tượng world quản lý toàn bộ việc cáp phát vùng nhớ cho các đối tượng này. Vì vậy đối tượng world khá quan trọng, dưới đây là một số tác dụng của đối tượng này có thể làm :

  • Định nghĩa trọng lực
  • Mô phỏng vật lý
  • Tìm các fixture trong một phạm vi cho trước
  • Tìm các fixture giao nhau
    Hai tác dụng cuối chúng ta sẽ bàn trong phần sau, bây giờ chúng ta sẽ cùng xem cách mà đối tượng world thực hiện hai phần đầu và vòng đời của một world. Một world được thiết lập giống như bất kì một class bình thường nào khác, đoạn code dưới đây mô tả cách khởi tạo một world :
b2Vec2 gravity(0, -9.8); //normal earth gravity, 9.8 m/s/s straight down!
bool doSleep = true;
b2World* myWorld = new b2World(gravity, doSleep);

Trọng lực được thiết lập có ảnh hưởng lên các đối tượng động trong world, và nó có thể được thay đổi bằng cách sử dụng phương thức SetGravity. Ví dụ, để tạo một môi trường vô trọng lực ta sẽ viết như sau :

m_world->SetGravity( b2Vec2(0,0) );

Biến sleep có nghĩa là một body có được phép “sleep” hay không nếu không có gì xảy ra với chúng (để tối ưu). Nếu nó được thiết lập là true, các body sẽ “sleep” khi không có gì xảy ra và sẽ được loại ra khỏi simulation cho đến khi có một cái gì đó đánh thức nó. Điều này có thể là va chạm với một body khcá, hoặc một lực tác dụng lên nó…
Lưu ý : Kể từ Box2D v2.2.1 thì biến sleep đã được loại bỏ và mặc định là true. Để thay đổi nó chúng ta có thể sử dụng phương thức b2World::SetAllowSleeping(bool).
Khi đối tượng world đã được tạo như trên, chúng ta có thể thêm một body vào. Chúng ta cần gọi phương thức Step của world để bắt đầu mô phỏng vật lý.

float32 timeStep = 1/20.0;      //the length of time passed to simulate (seconds)
int32 velocityIterations = 8;   //how strongly to correct velocity
int32 positionIterations = 3;   //how strongly to correct position
myWorld->Step( timeStep, velocityIterations, position iterations);

Trong ví dụ này, mỗi lần gọi phương thức step sẽ thực hiện simulation với 1/20 s, vì vậy một body sẽ di chuyển 5m/s. Biến timeStep sẽ ảnh hưởng đến cách mà trọng lực tác dụng lên mỗi body. Chúng ta có thể thấy sự thay đổi về gia tốc khi thay đổi biến timeStep. Để giống như vật lý thật, chúng ta sẽ thiết lập giá trị timeStep trùng với nố lần gọi phương thức Step() trong một giây. Ví dụ, FPS mặc định là 60, vì vậy phương thức Step() sẽ được gọi 60 lần vì vậy giá trị của timeStep sẽ là 1/60s.
Velocity iteration và position iteration ảnh hưởng đến cách mà body xảy ra khi có va chạm. Trong Box2D khi có sự va chạ xảy ra giữa 2 đối tượng, các object này sẽ nằm chồng lên nhau và một số tính toán cần thiết để tìm ra mỗi body sẽ di chuyển và xoay như thế nào để chúng không nằm chồng lên nhau nữa. Những biến này có giá trị càng cao thì tính chính xác càng đúng, cũng đồng nghĩa với việc phải xử lý nhiều hơn.
Dọn dẹp
Khi không sử dụng đối tượng world, chúng ta delete nó như con trỏ bình thường :

delete myWorld;

Khi một đối tượng world được hủy, nó sẽ xóa tất cả các body bên trong nó. Lưu ý rằng không được xóa các con trỏ body sau khi đã hủy đối tượng world.
Collision callbacks
Khi các body di chuyển xung quanh và va chạm lẫn nhau, Box2D sẽ xử lý tất các những gì cần thiết để phát hiện va chạm và xử lý chúng, vì vậy chúng ta không cần phải lo lắng về điều đó. Nhưng nếu trong trường hợp có một cái gì đó xảy ra trong game khi có một body va chạm một cái gì khác. Ví dụ khi player touch và một đối tượng monster và đối tượng đó sẽ chết, hoặc một quả bóng va chạm với mặt đất và tạo ra âm thanh. Chúng ta cần một cách để lấy thông tin về từ engine.
Trong phần vẽ một đối tượng, chúng ta đã tạo một tham chiếu đên smột body trong game, và lấy về tọa độ của chúng sau mỗi frame và vẽ chúng lên, bởi vì chúng ta phải vẽ body mỗi frame, và sau mỗi frame tọa độ và góc xoay của body đó luôn thay đổi. Nhưng va chạm thì khác một chút, chúng không xảy ra mỗi frame, chúng thường xảy ra ít hơn. Vì vậy lấy thông tin va chạm về từ engine sau mỗi frame khá tốn kém.
Trong box2D chúng ta xử lý việc này với một phương thức callback. Phương thức này được gọi khi có 2 đối tượng va chạm với nhau. Chúng ta vẫn không biết được hai đối tượng nào sẽ va chạm với nhau, vì vậy chúng sẽ được truyền vào phương thức như một đối số khi có va chạm xảy ra.
CallBack Timing
Va chạm trong world sẽ được phát hiện thông qua phương thức b2World::Step() mà chúng ta gọi trong mỗi vòng lặp. Như đã nói trong phần trước, phương thức b2World:Step() thiết lập thời gian, di chuyển tất cả các body. Khi một va chạm được phát hiện, phương thức callback sẽ thực hiện một số thứ, sau đó phương thức b2World::Step sẽ được tiếp tục xử lý. Chúng ta nên lưu ý là phương thức callback sẽ được gọi giữa quá trình của phương thức Step, chúng ta không được thay đổi scene lúc này, vì có thể vẫn còn nhiều va chạm xảy ra sau đó trong cùng một lần gọi Step.

Thiết lập một phương thức callback cũng giống như thiết lập debug draw. Chúng ta tạo một lớp con của lớp b2ContactListener, và sẽ overide các phương thức ảo của chúng. Dưới đây là các phương thức chúng ta sẽ sử dụng :

//b2ContactListener
// Called when two fixtures begin to touch
virtual void BeginContact(b2Contact* contact);
// Called when two fixtures cease to touch
virtual void EndContact(b2Contact* contact);

Lưu ý rằng chúng ta cũng có thể phát hiện khi một va chạm kết thúc với phương thức EndContact.
Callback information
Khi đã thiết lập một lớp con của b2ContactListener, chúng ta sẽ biết khi một cặp đối tượng va chạm với nhau. Đối số contact chứa tất cả các thông tin mà chúng ta cần về đối tượng va chạm. Cái chính là chúng ta cần quan tâm là 2 fixture nào va chạm với nhau, chúng ta có thể biết được như sau :

//b2Contact
// Get the first fixture in this contact
b2Fixture* GetFixtureA();
// Get the second fixture in this contact
b2Fixture* GetFixtureB();

Để lấy được body của một fixture chúng ta sử dụng phương thức GetBody() có trong fixture.

Trong ví dụ trên, dynamic body có 4 fixture, một trong số chúng va chạm với một fixture khác thuộc một static body. Đối số contact trong phương thức BeginContact sẽ trả về một trong hai fixture với GetFixtureA() và fixture kia với GetFixtureB(). Chúng ta cần phải kiểm tra để phân biệt rõ 2 fixture đó.