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.
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
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,
Hãy làm theo các bước sau
* FILE -> Add -> Existing Project
* 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"
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
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
// Đoạn này quan trọng nhất để app body trong Box2D
//---------------------KHUNG VAT LY BOX2D--------------------
//bodyDef
//ballBody
//-----------------------------------------------------------
scheduleUpdate(); // Update lại scene theo thời gian, phải có cái này nhé
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));
//---------------------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.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 = 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);
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);
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 )
Cám ơn bạn raaaaaaaaaaaaaaaaaaaaaaaaaat nhiều <3
ReplyDeletethanks bạn nhé :D
ReplyDeleteMình chưa hiểu các thuộc tính này lắm
ReplyDeleteb2World *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
Các thuộc tính này có thể dùng chung cho nhiều vật thể hay chỉ 1 vật thể? Và 1 vật thể phải có đủ tất các thuộc tính này hả bạn?
- Nếu mính không đặt trọng lượng, ma sát, đàn hồi thì có sao không? vì mình xóa bỏ dòng code đi vẫn không thay đổi gì cả?
Thanks
+ Cái gì là world thì dùng cho cả game
ReplyDelete+ Mấy cá body, fixture,v..v dùng cho từng vật thể
+ deltaTime : là biến thiên thời gian, dùng cho cả game, bạn hiểu 24 hình/giây trong điện ảnh rồi chứ => deltatime = 1/24 giây ( chiếu 1 hình )
mình làm y chang, cuối cùng vẫn là màn hình hello world của như lúc tạo prj
ReplyDeleteÔ, thế à, bạn không save khi add thư viện xong chăng?
Deleteđc roi bạn ơi, thanks bạn nhiu nha
DeleteMình viết từng dòng theo hướng dẫn của bạn trên v3.2 khi chạy nó chỉ hiện thi dc spriteball và hàm update(float dt) chỉ dc gọi 1 lần, rem hết vòng for lại thì chạy scheduleUpdate oke, Chắc nó ko get dc body rồi bạn check lai code hướng dẫn còn thiếu gì ko nha. Cảm ơn bài viết của bạn!
ReplyDeleteĐúng rồi, bài này 2 part,
DeletePart này chủ yếu giới thiệu cách sử dụng Box2D, nên mình đâu có cho hàm update chạy, mới để đấy thôi. Dành cho Part sau mà
Vẫn get body bình thường, tại vì ko có hàm gọi hàm update nên quả bóng đứng im.
Hãy coi tiếp bài sau nhé.!
Oke mình sẽ viết tiếp theo bài 16. Trong box2d mình thấy mô tả thuộc tính //fixtureDef
DeletefixtureDef.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
rõ ràng hơn bên chipmunk theo ý kiến riêng của mình. Cảm ơn bạn!
ball.png not found. please help me !
ReplyDeletemình mở file CMakeLists.txt trong Project ( dùng NotePad++). Tìm đến dòng 162 nhưng nó ko có hiện cái bảng như bạn nói
ReplyDeleteChắc bạn dùng bản COcos mới rồi,
DeleteĐể thêm thư viện Box2D, bạn chỉ cần dùng VS add thêm thư viện Existing Project là build được
Bạn có thể cho mình hỏi vấn đề này được không ạ??
ReplyDeleteMình thắc mắc chỗ world->Step(deltaTime, 10, 10);
Tại sao mình phải truyền 2 giá trị là 10, 10... Mình truyền giá trị khác có được ko?? Khi mình test mình để giá trị là world->Step(deltaTime, 1000, 10); thì nó không đi đúng theo quỹ đạo nữa ?? tại sao lại như thế ?? Cảm ơn ad