Hi mọi người!
Trong bài 13, mình đã giới thiệu qua với các bạn cách sử dụng phần mềm Physicsbody Editor để detect khung body physic từ 1 hình ảnh có dạng phức tạp. Phần mềm sẽ tạo ra 1 file .JSON để lưu thông tin về physics body đó. Công việc của chúng ta bây giờ là sử dụng file .JSON có sẵn để import vào game mà thôi.
Công việc trong phần này như sau:
+ Tìm hiểu một chút về cách phân tích file .JSON
+ Tạo ra physics body từ file JSON và import vào game.
Thế thôi nhỉ, chúng ta bắt đầu,
Đầu tiền, hãy down file nguồn ở đây nhé, file JSON mọi người tự tao như hướng dẫn ở bài trước nha. Mình chỉ up file Class lên thôi
B1 - Phân tích file .JSON
Bước này mình cũng nói luôn là chúng ta chỉ "Cưỡi ngựa xem hoa thôi nhé", vì mình thấy nó giống như là giải thích 1 số hàm sẵn có trong thư viện hàm vậy. Hãy mặc nhiên sử dụng có lẽ tốt hơn.. Thôi thì cứ ngó qua cho biết chút.
Mở file MyBodyParser.h , trong này khai báo 1 lớp MyBodyParser với các hàm cần thiết để phân tích file JSON, chủ yếu là mấy hàm đọc chuỗi, ví dụ
bool parseJsonFile(const std::string& pFile); // Đọc thông tin từ 1 file pFile
bool parse(unsigned char* buffer, long length); // Đọc thông tin vào 1 buffer có độ dài length
Và hàm quan trọng nhất, nên nhớ.
PhysicsBody* bodyFormJson(Node* pNode, const std::string& name); // Tạo ra 1 cái physic body và gắn cho Node nào đó ( thường là Sprite).
MyBodyParser.cpp sẽ định nghĩa các hàm ở trên nhé, chỗ này cũng khá phức tạp để giải thích ( trình cũng chưa tới nên cũng hơi bị khó hiểu ) Tạm thời chúng ta nhớ mấy hàm này về sau cứ thể sử dụng như 1 hàm trong thư viện có sẵn thôi. ( Tất nhiên là phải bê nguyên cả 2 file này vào Class của Project rồi)
B2 - Cách Import vào Game
Mở file HelloWorleScene.h nghiêng ngó chút
+ Trong này khai báo mấy hàm bắt sự kiện Touch
+ Khai báo 1 con trỏ Sprite*, 1 con trỏ LabelTTF*
+ Khai báo 1 hàm nodeUnderTouch(), để xác định vị trí Node khi Touch lên màn hình
Mở file HelloWorleScene.cpp
+ Phần Inlcude có #include "MyBodyParser.h"
+ Hãy chú ý từ phần này
sp_2dx = Sprite::create("2dx.png"); // Thêm vào 1 Sprite ảnh
sau đó là lệnh setPosition quen thuộc nhé.
Và chúng ta tập trung chú ý vào đoạn lệnh quan trọng sau
//Import file bodies.JSON để phân tích
MyBodyParser::getInstance()->parseJsonFile("bodies.json");
// Tạo ra 1 physic body bằng lệnh bodyFormJson của lớp MyBodyParser, hàm này nhận 2 đối số, 1 là sprite ở trên, 2 là tên của project tạo bởi Physicbody Editor ( hãy nhớ lúc ta dùng phần mềm có tạo 1 project là "2dx")
auto _body = MyBodyParser::getInstance()->bodyFormJson(sp_2dx, "2dx");
// Kiểm tra có tồn tại không?
if (_body != nullptr) {
_body->setDynamic(false); // Thiết lập body tĩnh, khi vật bị va chạm sẽ đứng im không nhúc nhích gì hết
_body->setCollisionBitmask(0x000000); // Thiết lập không va chạm vật khác
sp_2dx->setPhysicsBody(_body); // Đặt body vào sprite, đã quen thuộc
}
// Đặt Sprite vào layer của Scene
this->addChild(sp_2dx, 0);
Đoạn lệnh bên dưới là tạo Listener lắng nghe sự kiện Chạm màn hình, đã quá quen thuộc nhé, các bạn trở lại các bài 9-12 để nhớ lại.
Xong rồi các mem, với đoạn lệnh trên chúng ta đã biết cách đặt 1 khung physic body từ 1 file JSON vào đối tượng Sprite rồi nhé, quá đơn giản rồi ( Nếu ko xét lớp MyBodyParser). Tiếp theo chúng ta nâng cao hơn 1 chút là kiếm tra xem, có đúng là physic body áp vào có khớp với phần viền của sprite hay ko
+ Xét hàm Node* HelloWorld::nodeUnderTouch(cocos2d::Touch *touch)
auto location = this->convertTouchToNodeSpace(touch); // Lấy vị trí
auto scene = Director::getInstance()->getRunningScene(); // Lấy Scene đang chạy
auto arr = scene->getPhysicsWorld()->getShapes(location); // Trả về 1 mảng <PhysicsShape*> của các hình khối vật lý có tọa độ là location,
for (auto& obj : arr) // Vòng lặp kiểm tra trong mảng đó có Node nào là Sprite sp_2dx không
{
// Nếu có
if ( obj->getBody()->getNode() == sp_2dx)
{
node = obj->getBody()->getNode(); // Lưu lại node
break; // Kết thúc vòng lặp luôn nếu tìm thấy
}
}
return node; // Trả lại giá trị là node cho hàm
+ Xét hàm onTouchBegan()
auto current_node = nodeUnderTouch(touch); // Gọi hàm nodeUnderTouch để trà về 1 node
// Nếu nốt đó đúng là sp_2dx hoặc ko là sp_2dx thì "kêu lên" như sau
if (current_node == sp_2dx)
{
status_label->setColor(Color3B::GREEN); // đặt màu xanh
status_label->setString("Ohoo, DUNG CHAM VAO EM!");
}
else
{
status_label->setColor(Color3B::RED);
status_label->setString("Haha, RA NGOAI ROI ANH!");
}
return true; // phải trả về true
Còn hàm onTouchEnded, onTouchMoved thì đơn giản rồi, các bạn tự tìm hiểu nhé.
Bây giờ biên dịch và chạy thử xem thành quả nào
Kết thúc bài 14, chúng ta đã học được cách tạo physic body khá hay từ file JSON, và cách nạp nó vào game.
Ngoài cách import từ file JSON để tạo khung vật lý từ hình phức tạp, ta còn 1 cách khác là import từ file PLIST được tạo bởi phần mềm PHYSICS EDITOR, Hoặc Cocos Studio
Hẹn gặp lại các bạn ở bài sau!
Bài 15: Box2D - Một thư viện vật lý khác của Cocos2d-x (Part 1)
Ngoài cách import từ file JSON để tạo khung vật lý từ hình phức tạp, ta còn 1 cách khác là import từ file PLIST được tạo bởi phần mềm PHYSICS EDITOR, Hoặc Cocos Studio
Hẹn gặp lại các bạn ở bài sau!
Bài 15: Box2D - Một thư viện vật lý khác của Cocos2d-x (Part 1)
Nếu mình áp dụng khung PhysicsBody cho các con quái của bài tập 10,11,12 thì khi bắn bị lỗi, getNode() của va chạm bằng null.
ReplyDelete1 cái là PhysicsBody::createCircle va chạm với 1 cái MyBodyParser::getInstance()->bodyFormJson
Mình khác phục thế nào vậy bạn?
Vẫn bình thường mà bạn
DeleteMình thay đoạn này
auto targetBody = PhysicsBody::createCircle(target->getContentSize().width / 2);
target->setTag(2);
targetBody->setContactTestBitmask(0x1);
target->setPhysicsBody(targetBody);
Bằng đoạn này
MyBodyParser::getInstance()->parseJsonFile("quai.json");
auto _body = MyBodyParser::getInstance()->bodyFormJson(target, "quai");
if (_body != nullptr) {
_body->setContactTestBitmask(0x000001); // Lệnh setCollisionBitmask ko hoạt động
target->setPhysicsBody(_body);
target->setTag(2);
}
Đều là PhysicBody cả nên vẫn va chạm nhau bình thường, nhưng mình đang có một vấn đề về bộ nhớ, bắn một lúc tự Stop Working, có lẽ phải xử lý thêm. Nhìn chung là PhysicBody ok chạy tốt nhé. Bạn test lại xem
Đúng rồi, ý mình là cái stop working đó đấy. nó getNode() ra null.
DeleteDẫn tới stop working
getNode() ra null à, bạn debug bằng gì thế, mình dùng VS mà ko thấy?
Deletedùng VS2013, lúc đạn va chạm với quái. callback va chạm.
Deletebool HelloWorld::onContactBegin(const PhysicsContact& contact)
{
//Lấy đối tượng va chạm thứ nhất, ép kiểu con trỏ Sprite*
auto bullet = (Sprite*)contact.getShapeA()->getBody()->getNode(); //// Null ở đây
//Lấy giá trị cờ để xét xem đối tượng nào ( đạn, quái, hay nhân vật)
int tag = bullet->getTag();
//Lấy đối tượng va chạm thứ hai, ép kiểu con trỏ Sprite*
auto target = (Sprite*)contact.getShapeB()->getBody()->getNode();
//Lấy giá trị cờ để xét xem đối tượng nào ( đạn, quái, hay nhân vật)
int tag1 = target->getTag();
//Nếu va chạm xảy ra giữa đạn và quái thì xử lý xóa cả đạn và quái khỏi Layer trong Scene ( biến mất khỏi màn)
auto bullet , auto target mình đặt cho dễ nhìn vậy thôi, chứ đã biết cái nào là bullet, cái nào là target đâu, vai trò như nhau. Trong VS nó chỉ ra là Null, hay bạn đoán là Null?
DeleteNó chỉ ra null mà. do đó khi gọi bullet->getTag(); sẽ báo lỗi ngay, vì bullet null
DeleteĐương nhiên bullet ở đây có thể là đạn hoặc cũng có thể là quái
uhm, chua fix được lỗi này, có khi phải tìm cách khác
Deletebạn ơi sao vẽ lên cái hình lại có các đoạn thẳng xuyên qua thế nhỉ
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteBạn ơi làm thế nào để ẩn khung physics body lại chỉ còn hình ảnh không thôi?
ReplyDeleteTrong hàm CreateScene
DeleteThis comment has been removed by the author.
Deletesp_2dx = Sprite::create("2dx.png");
ReplyDeletesp_2dx->setPosition(Point(visibleSize.width/2, visibleSize.height/2));
MyBodyParser::getInstance()->parseJsonFile("image.json");
auto _body = MyBodyParser::getInstance()->bodyFormJson(sp_2dx, "image");
if (_body != nullptr) {
_body->setDynamic(false);
_body->setCollisionBitmask(0x000000);
_body->setContactTestBitmask(0x1);
sp_2dx->setPhysicsBody(_body); // set Body
}
else
{
PhysicsBody* physics = PhysicsBody::createCircle(sp_2dx->getContentSize().width/2);
physics->setContactTestBitmask(0x1);
physics->setDynamic(false);
sp_2dx->setPhysicsBody(physics);
}
Bạn cho minh hỏi sao đoạn code này _body lại trả về nullptr, mình đã tạo file trong Re đầu đủ rồi
This comment has been removed by the author.
DeleteAdmin ơi cho mình hỏi, mình tạo khung sau đó add vào thì khung vật lý không trùng với sprite, cách sửa lỗi như thế nào nhỉ?
ReplyDelete