Bài 15: Box2D - Một thư viện vật lý khác của Cocos2d-x ( Part 1 )

Người đăng: share-nhungdieuhay on Chủ Nhật, 18 tháng 5, 2014

Hi cả nhà!
Trong các bài trước, mình đã giới thiệu với mọi người về các physics cơ bản của Cocos2d-x 3.x, và các physics này mặc định sử dụng thư viện của Cocos2d-x 3.x đơn giản, đễ hiểu, gần gũi và dễ dùng. Hệ thống physics này được phát triển trên nền tảng của hệ thống physics Chipmunk ( bạn search GG nhé )

Ngoài ra bạn có thể sử dụng Chipmunk một cách độc lập với hệ thống Physics của cocos2d-x 3.x ( tất nhiên là nếu bạn đã quen sử dụng cú pháp, hàm của Chipmunk rồi ). Và ngoài Chipmunk bạn còn có 1 thư viện Vật lý khác cũng rất phổ biến là Box2D. Nếu để so sánh thì thật khó, vì mỗi thằng có 1 lợi điểm riêng, thôi thì 50 - 50 cho lành. Chừng nào lên Pro rồi thì so sánh ko muộn

Thể nào cũng có bạn thắc mắc, sao mà lắm Physic thế, sao ko phải là cái khác đi. Hix, thật không biết phải trả lời sao. Theo mình thì Physic là 1 phần cơ bản và quan trọng của game, có lẽ nó xếp đằng sau ý tưởng + thuật toán - tính toán để giải quyết vấn đề, rồi cuối cùng là các phần mắm muối như âm thanh, hiệu ứng, v.v... Cứ nên tìm hiểu đủ 2 thư viện Box2D và Chipmunk kỹ vào, ko thừa chút nào. 3 Thư viện vật lý này chỉ khác nhau về tập lệnh, còn lại thì khá giống nhau trong cách mô phỏng thế giới vật lý thật. 

Sau đây, mình sẽ giới thiệu với các bạn cách để sử dụng Box2D trong Cocos2d-x. Mặc định Cocos2d-x cài đặt cho người dùng sử dụng thư viện của Cocos2d-x + Chipmunk, nếu muốn sử dụng Box2D bạn phải làm một số công việc cụ thể để import nó vào trình biên dịch.

Nội dung chủ yếu của phần này là:
+ Cách thiết lập để sử dụng Box2D
+ Một bài tập physic nhỏ, áp dụng Box2D physic

Bắt đầu chém nào!

B1 - Thiết lập sử dụng Box2D

Vì mặc định Cocos2d-x bắt chúng ta sử dụng Chipmunk, nên chúng ta phải làm 1 số việc sau:
1/ Mở file CMakeLists.txt trong Project của chúng ta ( dùng NotePad++ nhé để nó hiện số dòng ). Tìm đến dòng 162, rồi thêm vào "Box2D" như hình đưới

(Ban đầu)


(Thêm vào box2d)

2/ Mở file physics.sln ( physics là tên Project của mình ) bằng VS2012 theo đường dẫn sau physics\proj.win32\physics.sln, 

- Để ý phía bên trái, chỉ có 3 thư viện cơ bản được IMPORT sẵn, Audio, Chipmunk, Cocos2d


Hãy làm theo các bước sau

*  FILE -> Add -> Existing Project


Bạn để ý đường dẫn bên dưới khi chọn Box2D project ( nó nằm trong đường dẫn physics\cocos2d\external\Box2D\proj.win32\Box2D.vcxproj )


Kết quả đây, đã import vào rồi, nhưng bạn chưa thể dùng ngay đâu, phải làm thêm 2 bước nữa



* Chuột phải vào Project ( physics) chọn Project Dependencies, tích vào Box2D




* Chuột phải vào Project physics chọn References, hiện lên bảng project Properties Page, Click tiếp vào nút Add New References, và tick vào ô Box2D, rồi OK là xong



* Cuối cùng bạn phải SAVE cái Solution này lại = Ctrl + S nhé, rồi kiểm tra bước cuối cùng Mở file physics.vcxproj theo đường dẫn sau physics\proj.win32\physics.vcxproj

Nếu thấy dòng sau thì nghĩa là Box2D đã được nạp vào Project Physics, bạn search "Box2D"


Nếu search không thấy Box2D trong file này nghĩa là bạn import không đúng ( do chưa SAVE solution ở trong VS2012 chẳng hạn ). Hãy làm lại các bước * ở trên là OK.

Sẽ có bạn thắc mắc là sao ko mở trực tiếp file này rồi add dòng trên vào, Bạn để ý thấy là có 1 dòng <Project>xyz ở dưới là 1 đoạn mã, mình cũng ko biết lấy ở đâu ra, chắc do VS quy định, nên khá khó tìm thông số này ở đâu. Thống nhất làm theo cách trên nhé, làm mấy lần quen ấy mà

Khi cần import 1 Thư viện nào đó, bạn cũng làm theo cách trên nhé.

B2 - 1 Bài physic nhỏ nhỏ thực hành

Tạo 1 Project mới mang tên Box2Dtest, ( bạn không nên đặt project là Box2D nhé, vì sẽ không import Box2D vào project = VS được)

>cocos new Box2Dtest -p com.vn.box2dtest -l cpp -d f:android/project

Mở file HelloWorldScene.h lên, làm những việc sau
+ Thêm vào #include "Box2D/Box2D.h" tại phần #include
+ Thêm USING_NS_CC;
+ Thêm đoạn code sau vào phần Public:

b2World *world; // World với physic
b2Body *ballBody ; // Body của bóng
b2BodyDef bodyDef; // Định nghĩa cái Body trên
b2FixtureDef fixtureDef; // Định nghĩa một số thuộc tính tĩnh: ma sát, độ đàn hồi, trọng lượng,v.v.
b2CircleShape bodyShape; // Hình khối của body

Sprite *ball; // Hình quả bóng
float deltaTime; // Biến tính thời gian

void addWall(float w,float h,float px,float py); // Tạo 1 khung Wall bao quanh màn hình để cho quả bóng va chạm

void update(float dt); // Update scene theo thời gian

Mở file HelloWorldScene.cpp, làm những việc sau
+ Thêm vào lệnh #define SCALE_RATIO 32.0 ( vì Box2D dùng đơn vị mm nên ta phải có hệ số chuyển đổi này từ pixel sang mm)
+ Trong hàm init() Xóa từ đoạn code this->addChild(label, 1); đến return true; sau đó thêm đoạn code này vào

b2Vec2 gravity = b2Vec2(0.0f,-10.0f); // Vector gia tốc ( dấu - là chỉ hướng xuống, vì trục y hướng lên trên)

world = new b2World(gravity); // Tạo world với vector gia tốc

    // Tạo 1 Sprite quả bóng
    ball = Sprite::create("ball.png");

    // Đặt vị trí giữa màn hình
    ball->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

// Đoạn này quan trọng nhất để app body trong Box2D

//---------------------KHUNG VAT LY BOX2D--------------------

bodyShape.m_radius = 45 / SCALE_RATIO; // Bán kính của khối body

//fixtureDef
fixtureDef.density=10; // Trọng lượng
fixtureDef.friction=0.8; // Ma sát
fixtureDef.restitution=0.6; // Đàn hồi
fixtureDef.shape=&bodyShape; // Trỏ vào bodyShape

//bodyDef
bodyDef.type = b2_dynamicBody; // Va chạm động
bodyDef.userData = ball; // gắn với Sprite ball

// Đặt vị trí, và nhớ chuyển đổi đơn vị
bodyDef.position.Set(ball->getPosition().x/SCALE_RATIO,ball->getPosition().y/SCALE_RATIO);

//ballBody
ballBody = world->CreateBody(&bodyDef); // Tạo Body
ballBody->CreateFixture(&fixtureDef); // Tạo các thuộc tính tĩnh
ballBody->SetGravityScale(10); // Đặt tỷ lệ gia tốc, càng cao rơi càng nhanh

//-----------------------------------------------------------

    // Đặt quả bóng vào layer của Scene
    this->addChild(ball, 0);

scheduleUpdate(); // Update lại scene theo thời gian, phải có cái này nhé

+ Xây dựng hàm update(float dt) như sau

void HelloWorld::update(float dt){
   int positionIterations = 10;  // Vị trí
   int velocityIterations = 10; // Vận tốc

   deltaTime = dt; // Bước thời gian

// Mô phỏng chuyển động vật lý theo thời gian, hãy nghiên cứu ở đây http://www.box2d.org/manual.html và đây http://www.iforce2d.net/b2dtut/worlds\

// Có thể hiểu thế này, mỗi Step xảy ra trong dt giây , dt này trong file AppDelegate.cpp định nghĩa = dòng lệnh director->setAnimationInterval(1.0 / 60); Bạn thử thay 1/60 = 1/1 xem, rơi cực chậm theo từng giây

   world->Step(dt, velocityIterations, positionIterations);  

// Duyệt tất cả body của world
   for (b2Body *body = world->GetBodyList(); body != NULL; body = body->GetNext())   
// Xét những body có gắn vào Sprite
     if (body->GetUserData()) 
     {  

       // Trả về sprite quả bóng ( có mỗi sprite trong bài này )
       Sprite *sprite = (Sprite *) body->GetUserData();  
// Đặt lại vị trí của Sprite này theo vị trí của body ( body sẽ bị rơi dần theo time), nhớ nhân RATIO để chuyển sang tọa độ pixel
       sprite->setPosition(Point(body->GetPosition().x * SCALE_RATIO,body->GetPosition().y * SCALE_RATIO));  
      // Đặt khả năng quay tròn
       sprite->setRotation(-1 * CC_RADIANS_TO_DEGREES(body->GetAngle())); 

     }  
    world->ClearForces(); // Xóa mọi áp đặt cho Body
    world->DrawDebugData();  // Không hiểu, chắc là debug

}   
Build rồi chạy thử thôi, nếu bạn thấy 1 quả bóng rơi xuống đáy màn hình là đã thành công, chúc mừng nhé



Vậy là trong bài này chúng ta đã làm quen với Box2D và cách thiết lập physics body trong Box2D. Các bài sau mình sẽ hướng dẫn cụ thể hơn bằng các game nhỏ áp dụng Box2D nhé.

P/S: 1 số lưu ý nho nhỏ trong Box2D

+ Phải có 1 hàm có tham số vào là thời gian, trong này là update (float dt)
+ Khi gắn body cho sprite thì vị trí của sprite luôn bị phụ thuộc vào vị trí của body
+ Cách nhớ Quy đổi tỷ lệ, ở đây ta có 2 hệ tọa độ: Tọa độ của màn hình, và hệ tọa độ của Box2D ( khó hình dung đấy, và hay nhầm ). Hãy nhớ như sau

- Khi thiết lập vị trí ( Position ), kích thước cho các đối tượng, thành phần của Box2D (body, shape)  nếu tham số vào là tọa độ màn hình thì phải chia cho tỷ lệ ( "/SCALE_RATIO" ), ví dụ:

bodyDef.position.Set(ball->getPosition().x/SCALE_RATIO,ball->getPosition().y/SCALE_RATIO);

- Ngượi lại để thiết lập vị trí, kích thước cho các đối tượng thành phần của màn hình ( sprite, label,v..v..) nếu tham số vào là tọa độ Box2D thì  luôn nhân với tỷ lệ (*SCALE_RATIO), ví dụ

  sprite->setPosition(Point(body->GetPosition().x * SCALE_RATIO,body->GetPosition().y * SCALE_RATIO));  


Chào và hẹn gặp lại ở bài sau!

Bài 16: Box2D - Một thư viện vật lý khác của Cocos2d-x - Nâng cao ( Part 2 )

{ 0 nhận xét... read them below or add one }

Đăng nhận xét