Search…

Box2D - Phần 1: Giới Thiệu - Một Số Thuật Ngữ và Khái Niệm

01/10/202011 min read
Giới thiệu engine xử lý vật lý Box2D, các khái niệm, cách thành phần liên quan, cách khởi tạo và thao tác với một số thành phần chính của Box2D trên Cocos2d-x 3.4

Box2D là một engine xử lý vật lý trong không gian 2 chiều. Bản thân Box2D sẽ tạo một thế giới ảo (một vùng không gian tính toán) gọi là physics world (thế giới vật lý), các vật thể được thêm vào thế giới này và Box2D sẽ đảm nhận việc:

  • Mô phỏng các tác động vật lý trên đơn đối tượng như trọng lực, tốc độ hoặc trên đa đối tượng như các đối tượng va chạm nhau
  • Mô phỏng các đối tượng có sự liên kết như chuyển động của dãy các vật được nối tiếp nhau
  • Tính toán sự va chạm và trả kết quả cho các bước xử lý logic tiếp theo
  • Hỗ trợ các thao tác phụ như ray casting ...

Thuật ngữ và các khái niệm

World

World đã là thực thể chính trong đó chứa tất cả bodies. Khi tạo hoặc xoá một bodies, cần sử dụng một phương thức của các đối tượng trên world để thực hiện việc này, do đó, world quản lý tất cả  các đối tượng bên trong. Các việc có thể làm cho một worlds.

  • Xác định lực hấp dẫn.
  • Điều chỉnh mô phỏng vật lý.
  • Tìm fixtures trong một khu vực nhất định.

Khởi tạo

auto myWorld = new b2World();

Thiết lập thông số cho world

b2Vec2 gravity(0, -9.8f); // vector quy định hướng và độ lớn trọng lực
bool doSleep = true; // vòng đời của một world

// Khởi tạo một world có tên là myWorld
auto myWorld = new b2World(gravity, doSleep);

Thiết lập lực hấp dẫn:

myWorld->SetGravity(b2Vec2(0.0f , 0.0f)); // Không có lực hấp dẫn

Sau khi physics world được tạo, các body sẽ được thêm trực tiếp vào world này.

Ngoài ra, để tiến hành chạy mô phỏng, Box2D yêu cầu hàm Step() phải được gọi tường minh tại mỗi bước update của chương trình. Tuỳ thuộc yêu cầu chi tiết của việc mô phỏng mà mỗi lần gọi Step() thì thời gian sẽ được tính như thế nào. Ví dụ khi đặt thông số timeStep là 1 / 20s = 0.05s thì mỗi lần gọi Step() Box2D sẽ giả lập đã 0.05s trôi qua, vận tốc các vật thể sẽ được tính toán theo thời gian này. 

float timeStep = 1/20.0f;      // thời gian mô phỏng mỗi bước gọi hàm Step()
int velocityIterations = 24;   // 
int positionIterations = 12;   // 
  
myWorld->Step(timeStep, velocityIterations, positionIterations);

Xóa một world

Một đối tượng world có thể bị xoá bằng cách gọi:

delete myWorld;

Khi một world bị xóa thì nó sẽ xóa tất cả các joints và bodies trong nó. Vì vậy không sử dụng các con trỏ bodies đã bị xóa sau đó.

Bodies

Bodies là các đối tượng cơ bản trong Physics Scene. Các thuộc tính của body bao gồm:

  • mass: khối lượng.
  • velocity: vận tốc
  • rotational inertia: quá trình quay, cần bao nhiêu lực để bắt đầu hoặc dừng quay.
  • angular velocity: vận tốc góc.
  • location: vị trí.
  • angle: góc xoay của đối tượng.

Ngay cả khi biết tất cả những đặc điểm của một đối tượng, vẫn không biết những gì nó trông giống như hoặc làm thế nào nó sẽ phản ứng khi nó va chạm với một đối tượng. Hãy tưởng tượng rằng một body có các thuộc tính của một đối tượng mà không thể xem (Vẽ) hoặc touch (Chạm).  Để xác định kích thước và hình dạng của một đối tượng cần phải sử dụng fixtures.

Có ba loại bodies: static, dynamic và kinematic.

Khai báo

b2BodyDef myBodyDef;
myBodyDef.type = b2_dynamicBody; // Thiết lập loại bodies là dynamic
myBodyDef.position.Set(24, 12); // Thiết lập vị trí ban đầu là x=24 và y=12
myBodyDef.angle = 0; //Thiết lập góc bắt đầu

Đó là đủ để xác định một định nghĩa cơ bản body. Hãy nhớ rằng một body không có bất kỳ kích thước, hình dạng, vì vậy không xác định những định nghĩa trên ở đây. Có thể tự hỏi tại sao nó đã không có khối lượng nào, cách thông thường cung cấp một khối lượng cho cơ thể một là bằng cách thêm fixtures vào nó. Bây giờ, hãy xem cách để tạo ra một body:

auto myBody = myWorld->CreateBody(&myBodyDef);

Để cung cấp cho một body kích thước, hình dáng và đặc điểm khác, cần thêm một fixtures vào cho nó. Fixtures sẽ được nói rõ hơn ở những mục tiếp theo.

Chú ý: Để lấy những thuộc tính của body bạn sử dụng các phương thức Get như: GetPosition()GetAngle(), ..

Thiết lập

Một số thuộc tính của body.

myBody->SetTransform(b2Vec2(10, 20 ), 1 );
  • Đoạn code ở trên sẽ làm cho myBody bắt đầu thay đổi về 10 đơn vị rộng, 20 đơn vị cao và xoay 1 radian ngược chiều kim đồng hồ. Box2D sử dụng radian cho phép đo góc, vì vậy, nếu sử dụng đơn vị góc độ có thể làm như sau:
#define DEGTORAD 0.0174532925199432957f
#define RADTODEG 57.295779513082320876f
  
// 45 độ cùng chiều với kim đồng hồ.
dynamicBody->SetTransform( b2Vec2( 10, 20 ), 45 * DEGTORAD ); 

Có thể thiết lập các thuộc tính như vận tốc và vận tốc góc của body như sau:

myBody->SetLinearVelocity( b2Vec2(-5.0f , 5.0f ) ); //di chuyển lên với trái 5.0 đơn vị.
myBody->SetAngularVelocity(-90 * DEGTORAD ); //quay 90 độ ngược chiều kim đồng hồ.

Static body

Khởi tạo:

myBodyDef.type = b2_staticBody; //khởi tạo cho myBody là một static body
myBodyDef.position.Set(0, 10); 

b2Body* staticBody = myWorld->CreateBody(&myBodyDef); 
staticBody->CreateFixture(&boxFixtureDef);// tạo fixtuare cho myBody

Ở đây, sẽ tạo ra một body, nhưng nó sẽ không di chuyển. Có nghĩa chính xác khi tạo ra một static body trong dự án, nó sẽ dứng im và không bị tác động như  lực hấp dấn, va chạm với các body khác,... khi cho nó ở vị trí nào thì nó sẽ ở vị trí đó.

Kinematic body

Cho đến lúc này, có thể di chuyển một dynamic body và không thể di chuyển một static body. Khi một static body và một dynamic body va chạm với nhau, static body luôn đứng im dù va chạm có mạng đến mức nào, dynamic body sẽ va chạm đi đâu đó. Và cả hai không thể chồng lên nhau. Kinematic body cũng giống với static body với ví dụ ở trên nhưng điều khác biệt giữa chúng là có thể làm cho kinematic body di chuyển.

myBodyDef.type = b2_kinematicBody;
myBodyDef.position.Set(-18, 11); 
b2Body* kinematicBody = myWorld->CreateBody(&myBodyDef); 
kinematicBody->CreateFixture(&boxFixtureDef); 
 
kinematicBody->SetLinearVelocity( b2Vec2( 1, 0 ) ); 
kinematicBody->SetAngularVelocity( 360 * DEGTORAD ); 

Body trong đoạn mã trên có thể di chuyển và xoay, nó không bị ảnh hưởng với lực hấp dẫn, và không bị ảnh hưởng khi một dynamic body va chạm. Trong dự án games, có thể sử dụng kinematic body cho các nhân vật và đối tượng trong cảnh. Static body thường sử dụng cho các bức tường, sàn nhà,....

Bodies list

Nếu muốn lấy tất cả bodies trong world hiện tại của bạn. Phương thức GetBodyList() trả về body đầu tiên trong danh sách bodies ở trong world.

for ( auto b = myWorld->GetBodyList(); b; body = b->GetNext())
{
      // xử lý với body vừa lấy được
}

Thường sẽ sử dụng đến nó để thao tác logic trong game, và thường được sử dụng trong hàm update() trong Scene hiện tại.

Xóa một body

Khi muốn xóa một body, sử dụng phương thức DestroyBody()

myWorld->DestroyBody(myBody);

Fixtures

Được sử dụng để mô tả các kích thước, hình dạng, và các đặc tính của body trong physics world. Một body có thể có nhiều fixtures gắn lên, và vì vậy đặc tính của body sẽ bị ảnh hưởng, và khi các bodies va chạm với nhau cũng bị ảnh hưởng. Các thuộc tính chính của fixtures:

  • Density: Giá trị liên quan giữa khối lượng với diện tích.
  • Friction: Giá trị ma sát.
  • Restitution: Giá trị đàn hồi.
  • Shape: Hình dạng của body. Là một đa giác hoặc vòng tròn.

Chú ý: Hình dạng của body là đa giác lồi, tức là đa giác ở đây có các góc của nó không lớn hơn một góc 180 độ.

Shape

Là hình dạng mô tả va chạm hình học, bằng cách gắn các hình dạng cho body. Khi cần để xác định một hình dạng phức tạp, có thể đính kèm nhiều hình dạng để một body duy nhất.

Thuộc tính của các Shape

  • Type: mô tả các hình dạng, chẳng hạn như vòng tròn, hộp, đa giác...
  • Area: diện tích của bodies,  được sử dụng để tính toán các thuộc tính khối lượng của cơ thể, mật khộ và khu vực cung cấp cho đối tượng.
  • Mass: khối lượng của bodies.
  • Offset: xác định mô-men xoắn cần thiết cho một gia tốc góc mong muốn.
  • Moment
  • Tag: được sử dụng để xác định hình dạng.

Khai báo và thiết lập một hình dạng vòng tròn.

b2CircleShape circleShape;
circleShape.m_radius = 10,0f; 

// Khai báo fixtureDef;
b2FixtureDef myFixtureDef;
myFixtureDef.shape = &circleShape; // đây là con trỏ chỉ tới hình dạng ở bên trên đã khai báo.

b2BodyDef myBodyDef;
myBodyDef.position.Set(100.0f, 100.0f);
myBodyDef.type = b2_dynamicBody;

auto myBody = myWorld->CreateBody(&myBodyDef)
myBody->CreateFixture(&myFixtureDef); //thêm một fixture vào body

Khởi tạo một hình đa giác có 5 đỉnh:

//thiết lập mỗi đỉnh của đa giác trong một mảng.
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); //dùng mảng ở trên để tạo hình dáng.
  
myFixtureDef.shape = &polygonShape; //gắn hình dạng cho body
myBodyDef.position.Set(100.0f, 100.0f); 
b2Body* dynamicBody2 = myWorld->CreateBody(&myBodyDef);
dynamicBody2->CreateFixture(&myFixtureDef); //thêm fixture cho body

Chú ý khi tạo một hình dạng đa giác theo cách này.

  1. Giới hạn các đỉnh của đa giác là 8. Nếu cần nhiều hơn nữa có thể điều chỉnh giá trị b2_maxPolygonVertices trong tập tin b2Settings.h. Các đỉnh phải được xác định theo thứ tự ngược chiều kim đồng hồ, và luôn luôn là một đa giác lồi. 
  2. Nếu muốn một vật cố hình chữ nhật, cách dễ nhất để có được một trong số đó là với các chức năng SetAsBox.
polygonShape.SetAsBox(4, 2); //một hinhd chữ nhật 4x2 đơn vị.
myBodyDef.position.Set(100.0f, 100.0f);
  
b2Body* dynamicBody3 = myWorld->CreateBody(&myBodyDef);
dynamicBody3->CreateFixture(&myFixtureDef);

Chú ý rằng các thông số của SetAsBox1/2 chiều rộng1/2 chiều cao của hình chữ nhật.

Mulitiple fixtures

Một body có thể được gắn nhiều fixture. Ví dụ một về một body được gắn 4 fixtures.

// www.stdio.vn  
//thiết lập body static
b2BodyDef myBodyDef;
myBodyDef.type = b2_dynamicBody;
myBodyDef.position.Set(100.0f, 100.0f);
b2Body* dynamicBody = myWorld->CreateBody(&myBodyDef);
    
//thiết lập một hình dạng
b2PolygonShape polygonShape;
b2FixtureDef myFixtureDef;
myFixtureDef.shape = &polygonShape;
myFixtureDef.density = 1;
    
//thêm 4 fixture hình dạng vuông quanh trung tâm body
for ( int index = 0; index < 4; index++) {
      b2Vec2 pos( sinf(i*90*DEGTORAD), cosf(i*90*DEGTORAD) );
      polygonShape.SetAsBox(1, 1, pos, 0 ); 
      dynamicBody->CreateFixture(&myFixtureDef)
}

Density

Mật độ của một vật cố định nhân theo khối lượng của body đó. 

myFixtureDef.density = 1000.0f;

Friction

Giá trị ma sát của một body, thiết lập giúp cho các vật trượt trên nhau.

myFixtureDef.density = 1.0f;

Density có giá trị từ 0 đến 1. Với 1 là đảm bảo chắc chắn rằng vật sẽ không trượt trên nhau.

Restitution

Giá trị đàn hồi của một body.

myFixtureDef.restitution= 1.0f;

Chú ý

  • Restitution có giá trị từ 0 đến 1 với 1 là đàn hồi hoàn toàn.
  • Giá trị 0 bồi thường không luôn luôn đảm bảo rằng sẽ có không có hiện tượng đàn hồi.
  • Trên thực tế một số lượng nhỏ của năng lượng có thể bị mất trong đàn hồi.

Thay đổi giá trị fixtures

Thay đổi các giá trị của fixtures như sau:

myFixtureDef->SetDensity(2.0f); // denisty
myFixtureDef->SetRestitution(1.0f); // restitution
myFixtureDef->SetFriction(1.0f); // friction

Fixtures list

Nếu bạn muốn xem tất cả các fixtures trên một body, bạn có thể dễ dàng thực hiện như sau:

for (auto f = body->GetFixtureList(); f; f = f->GetNext())
{
      // xử lý với fixture vừa lấy được.
}

Xóa một fixture

// xóa một fixture trong một body
myBody->DestroyFixture(myFixtureDef);

Chú ý

  1. Không sử dụng con trỏ sau khi body chứa nó đã bị xóa sau đó. Xóa bỏ một body, tất cả các fixtures được gắn trên nó sẽ bị xóa.
  2. Nếu một dự án có một logic game phức tạp với việc sử dụng và xóa liên tục, cần thật sự cẩn thận với việc thiết kế và quản lý trong dự án.

Chú ý

  • Đơn vị đo lường của Box2D không phải là pixel mà là PTM. 
  • Đơn vị tính góc của Box2D là radian.
IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024