Game Studio
Liên kế mạng xã hội

Game Studio


Box2D Với Cocos2d-x 3.x.x Phần I: Giới Thiệu Và Một Số Thuật Ngữ Và Khái Niệm

Giới thiệu

Bạn sẽ làm gì khi ra trò chơi của bạn cần mô phỏng tình huống như trong thế giới thực? Bạn phải cần biết phát hiện va chạm, lực hấp dẫn, độ đàn hồi và ma sát... Trong bài viết này tôi và các bạn sẽ tìm hiểu về tạo game vật lý trong Cocos2d-x 3.4. Tôi tin việc tạo hẳn ra một game vật lý với Cocos2d-x sẽ là một điều gì đó đặc biệt với bạn.

Tiền đề bài viết

Bài viết này là bài viết nằm trong những loạt bài giới thiệu và tổng quan làm game 2D sử dụng Cocos2d-x 3.4.

Đối tượng hướng đến

Bài viết này tôi hướng đến những lập trình viên mới bắt đầu tìm hiểu và học tập làm game 2D bằng Cocos2d-x.

Box2D là gì?

Là một physics world. Các vấn đề cơ bản của việc sử dụng physics world trong game 2D:

  • Phát hiện va chạm giữa các đối tượng trong trò chơi của bạn, mô phỏng các lực và kết quả của chuyển động của các đối tượng từ những va chạm...
  • Mô phỏng các lực và kết quả chuyển động của các đối tượng trong trò chơi của bạn từ những va chạm...

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

Trước khi bước tôi giới thiệu cho bạn các phần tiếp theo, để dễ dàng học tập, bạn hãy tưởng tượng rằng môi trường xung quanh bạn là world, tất cả những thứ ở trong môi trường bạn có hiện giờ như laptop, keyboard, mouse, hay chính bản thân bạn là những body.

World

World đã là thực thể chính trong đó chứa tất cả bodies. Khi bạn tạo hoặc xoá một bodies, bạ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 đang quản lý tất cả các phân bổ cho các đối tượng bên trong nó. Bạn có thể làm những điêu gì với 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

  1. auto myWorld = new b2World();

Thiết lập và khởi tạo một world

  1. b2Vec2 gravity(0, -9.8f); // Lực hấp dẫn của Trái Đất
  2. bool doSleep = true; // vòng đời của một world
  3.  
  4. // Khởi tạo một world có tên là myWorld
  5. auto myWorld = new b2World(gravity, doSleep);

Bạn có thể thiết lập lực hấp dẫn:

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

Tham số sleep nó cho phép bodies được nghỉ ngơi, và bị loại trừ khỏi các mô phỏng cho đến khi một điều gì đó sẽ xảy ra để đánh thức chúng một lần nữa. Có thể là một va chạm hoặc một lực tác động lên nó.
Chú ý: tham số sleep mặc định để True. Để thay đổi này, bạn sử dụng:

  1. b2World::SetAllowSleeping(bool).

Khi bạn đã tạo một world, bạn có thể thêm bodies vào nó.Bạn muốn không gian vật lý của bạn luôn luôn hoạt động thì bạn cần phải liên tục gọi hàm Step() của world để chạy mô phỏng vật lý.

  1. float timeStep = 1/20.0f; // thời gian trôi qua dùng để mô phỏng (giây)
  2. int velocityIterations = 24; //
  3. int positionIterations = 12; //
  4. myWorld->Step(timeStep, velocityIterations, positionIterations);
Phân tích:

Mỗi lần Step() sẽ mô phỏng bằng 1/20 giây, do đó, một bodydi chuyển vào 5m/s như trong cảnh đầu tiên của bạn sẽ di chuyển 5/20 = 0,25 m trong thời gian đó. Để thực hiện một mô phỏng thực tế đang tìm kiếm, bạn thường sẽ đặt giá trị timeStep để phù hợp với số lần mỗi giây, bạn sẽ gọi phương thức Step() trong đối tượng world kèm chức năng trong trò chơi của bạn. Ví dụ trong thử nghiệm, framerate mặc định là 60 khung hình / giây, do đó, Step() được gọi là 60 lần một lần thứ hai với timeStep thiết lập để 1/60 giây.

Xóa một world

Để xóa một đối tượng world bạn có thể dễ dàng làm như sau:

  1. delete myWorld;

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

Bodies

Nó đã được nhắc tới ở mục Worlds. Bodies là các đối tượng cơ bản trong Physics Scene.

Các thuộc tính 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:.

Ngay cả khi bạn biết tất cả những đặc điểm của một đối tượng, bạn 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. Bạn 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à bạn 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 chúng ta cần phải sử dụng fixtures.

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

Khai báo

  1. b2BodyDef myBodyDef;
  2. myBodyDef.type = b2_dynamicBody; // Thiết lập loại bodies là dynamic
  3. myBodyDef.position.Set(24, 12); // Thiết lập vị trí ban đầu là x=24 và y=12
  4. 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 bạn không xác định những định nghĩa trên ở đây. Bạn 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ờ, bạn hãy xem cách để tạo ra một body:

  1. 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, bạn 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

Bây giờ tôi sẽ giới thiệu cho các bạn một số thuộc tính của body.

  1. 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 bạn giống tôi ở trong trường hợp này sử dụng đơn vị góc độ bạn có thể làm như sau:
  1. #define DEGTORAD 0.0174532925199432957f
  2. #define RADTODEG 57.295779513082320876f
  3. // 45 độ cùng chiều với kim đồng hồ.
  4. dynamicBody->SetTransform( b2Vec2( 10, 20 ), 45 * DEGTORAD );

Bạn 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:

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

Static body

Khởi tạo:

  1. myBodyDef.type = b2_staticBody; //khởi tạo cho myBody là một static body
  2. myBodyDef.position.Set(0, 10);
  3.  
  4. b2Body* staticBody = myWorld->CreateBody(&myBodyDef);
  5. 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. Tôi tin rằng khi bạn thử thiết lập các thuộc tính tốc độ,... cho một static body sẽ không có tác dụng. Có nghĩa chính xác khi bạn tạo ra một static body trong dự án của bạ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,... bạn cho nó ở vị trí nào thì nó sẽ ở vị trí đó.

Kinematic body

Cho đến lúc này, bạn 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.Kinamatic 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à bạn có thể làm cho kinematic body di chuyển.

  1. myBodyDef.type = b2_kinematicBody;
  2. myBodyDef.position.Set(-18, 11);
  3. b2Body* kinematicBody = myWorld->CreateBody(&myBodyDef);
  4. kinematicBody->CreateFixture(&boxFixtureDef);
  5. kinematicBody->SetLinearVelocity( b2Vec2( 1, 0 ) );
  6. 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ủa bạn, bạn 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 bạn 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.

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

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

Xóa một body

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

  1. 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 bạn cần để xác định một hình dạng phức tạp, bạn 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.
Các loại Shape:
  • PhysicsShape: hình dạng thực hiện lớp cơ sở PhysicsShape.
  • PhysicsShapeCircle: hình dạng vòng tròn dạng rắn. 
  • PhysicsShapePolygon: hình dạng đa giác lồi dạng rắn.
  • PhysicsShapeBox: hình dạng hộp dạng rắn.
  • PhysicsShapeEdgeSegment: hình dạng phân đoạn.
  • PhysicsShapeEdgePolygon: hình dạng đa giác dạng rỗng. Bao gồm nhiều PhysicsShapeEdgeSegment.
  • PhysicsShapeEdgeBox: hinh dạng hộp dạng rỗng. Bao gồm bốn PhysicsShapeEdgeSegment.

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

  1. b2CircleShape circleShape;
  2. circleShape.m_radius = 10,0f;
  3.  
  4. // Khai báo fixtureDef;
  5. b2FixtureDef myFixtureDef;
  6. myFixtureDef.shape = &circleShape; // đây là con trỏ chỉ tới hình dạng ở bên trên đã khai báo.
  7.  
  8. b2BodyDef myBodyDef;
  9. myBodyDef.position.Set(100.0f, 100.0f);
  10. myBodyDef.type = b2_dynamicBody;
  11.  
  12. auto myBody = myWorld->CreateBody(&myBodyDef)
  13. myBody->CreateFixture(&myFixtureDef); //thêm một fixture vào body

Bây giờ tôi sẽ khởi tạo một hình đa giác có 5 đỉnh:

  1. //thiết lập mỗi đỉnh của đa giác trong một mảng.
  2. b2Vec2 vertices[5];
  3. vertices[0].Set(-1, 2);
  4. vertices[1].Set(-1, 0);
  5. vertices[2].Set( 0, -3);
  6. vertices[3].Set( 1, 0);
  7. vertices[4].Set( 1, 1);
  8. b2PolygonShape polygonShape;
  9. polygonShape.Set(vertices, 5); //dùng mảng ở trên để tạo hình dáng.
  10. myFixtureDef.shape = &polygonShape; //gắn hình dạng cho body
  11. myBodyDef.position.Set(100.0f, 100.0f);
  12. b2Body* dynamicBody2 = myWorld->CreateBody(&myBodyDef);
  13. dynamicBody2->CreateFixture(&myFixtureDef); //thêm fixture cho body

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

  1. Chúng ta có giới hạn các đỉnh của đa giác là 8. Nếu bạn cần nhiều hơn nữa bạn 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 bạn 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.
  1. polygonShape.SetAsBox(4, 2); //một hinhd chữ nhật 4x2 đơn vị.
  2. myBodyDef.position.Set(100.0f, 100.0f);
  3. b2Body* dynamicBody3 = myWorld->CreateBody(&myBodyDef);
  4. dynamicBody3->CreateFixture(&myFixtureDef);

Chú ý rằng các thông số của SetAsBox là '1/2 chiều rộng' và '1/2 chiều cao' của hình chữ nhật.

Mulitiple fixtures

Như tôi giới thiệu ở trên, một body có thể được gắn nhiều fixture. Tôi sẽ ví dụ một về một body được gắn 4 fixtures.

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

Density

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

  1. 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.

  1. 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.

  1. 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.

Bạn có thể thay đổi các giá trị của fixtures như sau:

  1. myFixtureDef->SetDensity(2.0f); // denisty
  2. myFixtureDef->SetRestitution(1.0f); // restitution
  3. 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:

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

Xóa một fixture

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

Chú ý:

  1. Không sử dụng con trỏ sau khi body chứa nó đã bị xóa sau đó! Khi bạn 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ủa bạn có một logic game phức tạp với việc sử dụng và xóa liên tục, bạn thật sự cần thận với việc thiết kế và quản lý trong dự án của bạn.

Tổng kết

Có thể với những người mới, những điều tôi viết ở trên có lẽ là trừu tượng và khó nắm bắt. Bạn nên nghĩ đơn giản rằng:

  1. Một scene là một physics world  dùng để chứa các physics body.
  2. Bạn khởi tạo một physics body trong physics world với body def.
  3. Bạn thêm các thuộc tính fixtures vào physics body. 

Với việc bạn đã có thể tạo một body trong physics world ở dự án của bạn. Trong phần tiếp theo tôi sẽ tiếp tục giới thiệu một số thuật ngữ và khái niệm mới trong Box2D như tác động một lực lên một body, hay va chạm giữa các body.

Tham khảo

http://www.cocos2d-x.org

Theo: Stdio


Đăng sự kiện cho developer