Physics Editor
Physics Editor là một phần mềm hỗ trợ tạo ra textures cho những sprite có những hình dạng phức tạp. Physics Editor sẽ tạo ra một file format plist, các file format này được sử dụng để tạo fixtures cho body trong games. Nhiệm vụ lúc này là thiết lập các bodyDef cho body. Download và cài đặt phần mềm tại đây.
Tạo Physics Body cho Sprite
Sau khi cài đặt, khởi động PhysicsEditor, sử dụng Physics Editor để tạo fixtures cho sprite theo các bước sau:
- Thêm sprites: chọn sprite cần tạo body
- Theo dõi các phác thảo của sprites: tùy chọn để phác thảo ra các fixtures ưng ý.
- Đặt cấu hình các thông số vật lý: cài đặt một số thông số như density,...
- Exportes: chọn file format là plist.
Kết quả của việc tạo fixtures cho các sprite:
- Tạo ra một file định dạng .plist.
- Tạo ra một file định dạng .pes.
Giá trị đầu tiên
Sau khi cài đặt và mở phần mềm Physics Editor, chú ý những điểm chính sau:
- Exporter: Định dạng file format. Ở ví dụ này, chọn Box2D generic (PLIST).
- PTM: Box2D có bao nhiêu điểm giá trị PTM điểm ảnh là tương đương với 1 mét. Đây là đơn bị nội bộ đo lường của Box2D. Ở đây là 32 PTM.
- Add Sprite: Thêm sprite cần tạo fixtures ở đây. Ngoài ra, có thể chọn các sprite bằng cách kéo thả từ ngoài thư mục vào nơi chứa sprite.
- Publish As: Sau khi tạo xong các fixtures, chọn button để xuất ra file format.
- Anchor Point: Anchor point của body.
- Tool tạo Shapes: Chứa các button giúp tạo hình dáng cho body.
Tạo shapes thủ công
- Trên thanh tool, tạo Shapes giúp hỗ trợ việc tạo hình dáng của body cho sprite, chọn Add Polygon hoặc Add Circle, lúc này trên màn hình trung tâm sẽ xuất hiện một tam giác có 3 điểm điểm nút trắng hoặc một đường tròn với một nút trắng. 1 body có thể có nhiều fixtures.
- Kéo thả sao cho phù hợp với mục đích thiết kế hình dạng cho body. Khi muốn thêm một đỉnh để tăng số lượng cạnh của đa giác thì double click chuột vào gần một đoạn trong nó.
Tạo shapes bằng Tracer
Trên thanh tool tạo Shapes, chọn Shape tracer. Tracer giúp tự động tạo shape của body cho sprite.
*Quan trọng nhất ở đây là Tolerance, giá trị này cho đánh dấu chính xác làm thế nào đa giác nên phù hợp với hình dạng của Sprite, có ảnh hưởng tới số lượng đỉnh trên đa giác.
Thiết lập category, mask và thông số vật lý
Category và mask
- Category: mô phỏng các loại hình của một đối tượng. Ví dụ: nhóm đối tượng danger và nhóm đối tượng peave. Category không quyết định việc các đối tượng có va chạm với nhau.
- Mask: mô tả mà các đối tượng có thể va chạm. Ví dụ: Category có 2 nhóm là danger và peace. Project game có một enemy thuộc nhóm danger, để enemy có thể va chạm với những đối tượng khác cùng nhóm, chọn Mask cùng nhóm và ngược lại. Tương tự với việc quyết định va chạm với nhóm peace.
Tìm hiểu rõ hơn về 2 thông số category và mask ở phần Collision filtering.
Thông số vật lý
- Density là giá trị liên quan đến khối lượng.
- Friction là giá trị ma sát dùng cho các vật trượt lên nhau.
- Restitution là giá trị về đàn hồi.
Tìm hiểu rõ hơn về các thông số vật lý ở phần Fixtures.
Sau khi tạo shape body cho sprite, tiến hành Publish, Exit và tạo ra 2 file định dạng .plist và .pes.
* Khi tạo body cho những đối tượng có hình dạng phức tạp, hãy đảm bảo kích thước sử dụng sprite là không thay đổi.
Ví dụ
Tạo project mới:
- Copy file định dạng .plist và .pes vào Resource trong project.
- Download và copy vào Class trong dự án, bộ nạp GB2ShapeCache-x để có thể sử dụng các định dạng .plist là .pes vừa tạo.
- Download và copy vào Class trong dự án của bạn GLES-Render để vẽ hình dáng body của đối tượng lên màn hình
GB2ShapeCache-x:
GLES-Render:
Header
#include "cocos2d.h" #include "Box2D\Box2D.h" #include "GB2ShapeCache-x.h" #include "GLES-Render.h" USING_NS_CC; class HelloWorld : public cocos2d::Layer { public: static cocos2d::Scene* createScene(); virtual bool init(); void menuCloseCallback(cocos2d::Ref* pSender); // Tạo sprite void addNewSpriteWithCoords(); // Vẽ shapes của body virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override; void update(float dt); CREATE_FUNC(HelloWorld) private: b2World* world; //dùng để vẽ shapes body GLESDebugDraw *debugDraw; }; #endif // __HELLOWORLD_SCENE_H__
Source
#include "HelloWorldScene.h" USING_NS_CC; using namespace std; #define PTM_RATIO 32 //chuỗi chứa tên các sprite string names[] = { "obstacle_1", "obstacle_2", "obstacle_3", "tank_enemi_1", "tank_enemi_2", "tank_enemi_3", "tank_player" }; Scene* HelloWorld::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } bool HelloWorld::init() { if (!Layer::init()) { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); Size screenSize = Director::getInstance()->getWinSize(); // Khởi tạo physics world b2Vec2 gravity; gravity.Set(0.0f, -10.0f); bool doSleep = true; world = new b2World(gravity); world->SetAllowSleeping(doSleep); world->SetContinuousPhysics(true); //debug khung body this->debugDraw = new GLESDebugDraw(PTM_RATIO); this->world->SetDebugDraw(debugDraw); uint32 flags = 0; flags += b2Draw::e_shapeBit; flags += b2Draw::e_jointBit; //flags += b2Draw :: e_aabbBit; //flags += b2Draw :: e_pairBit; //flags += b2Draw::e_centerOfMassBit; //bạn thử bỏ comment và xem thử nó vẽ shapes body như thế nào this->debugDraw->SetFlags(flags); //******************************************************* //Tạo Ground bao quanh màn hình b2BodyDef groundBodyDef; groundBodyDef.position.Set(screenSize.width / 2 / PTM_RATIO, screenSize.height / 2 / PTM_RATIO) b2Body* groundBody = world->CreateBody(&groundBodyDef); b2PolygonShape groundBox; // bottom groundBox.SetAsBox(screenSize.width / 2 / PTM_RATIO, 0, b2Vec2(0, -screenSize.height / 2 / PTM_RATIO), 0); groundBody->CreateFixture(&groundBox, 0); // left groundBox.SetAsBox(0, screenSize.height / 2 / PTM_RATIO, b2Vec2(-screenSize.width / 2 / PTM_RATIO, 0), 0); groundBody->CreateFixture(&groundBox, 0); // right groundBox.SetAsBox(0, screenSize.height / 2 / PTM_RATIO, b2Vec2(screenSize.width / 2 / PTM_RATIO, 0), 0); groundBody->CreateFixture(&groundBox, 0); //Nạp file sprite sheet của các sprite bên trên đã được tạo body SpriteFrameCache::getInstance()->addSpriteFramesWithFile("SpriteSheet.plist", "SpriteSheet.png"); //nạp file .plist chứa các body mới tạo ở trên. //chú ý là file .plist và .pes phải cùng tên với nhau GB2ShapeCache::sharedGB2ShapeCache()->addShapesWithFile("shapedefs.plist"); schedule(schedule_selector(HelloWorld::update)); return true; } void HelloWorld::update(float dt) { //cứ 1s là tạo một sprite scheduleOnce(schedule_selection(HelloWorldScene::addObject), 1.0f); int velocityIterations = 8; int positionIterations = 1; world->Step(dt, velocityIterations, positionIterations); for (b2Body *body = world->GetBodyList(); body != NULL; body = body->GetNext()) if (body->GetUserData()) { Sprite *sprite = (Sprite *)body->GetUserData(); sprite->setPosition(Point(body->GetPosition().x * PTM_RATIO, body->GetPosition().y * PTM_RATIO)); sprite->setRotation(-1 * CC_RADIANS_TO_DEGREES(body->GetAngle())); } world->ClearForces(); world->DrawDebugData(); } void HelloWorld::addObject() { string name = names[rand() % 7]; // //khởi tạo sprite, set position và thêm vào scene hiện tại auto sprite = Sprite::createWithSpriteFrameName((name + ".png").c_str()); sprite->setPosition(Point(visibleSize.width/2, visibleSize.height/2)); addChild(sprite); // Tạo b2BodyDef b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; bodyDef.position.Set(p.x / PTM_RATIO, p.y / PTM_RATIO); bodyDef.userData = sprite; b2Body *body = world->CreateBody(&bodyDef); //tạo fixture cho body, bằng cách sử dụng GB2ShapeCache GB2ShapeCache *fixtureBody = GB2ShapeCache::sharedGB2ShapeCache(); fixtureBody->addFixturesToBody(body, name.c_str()); sprite->setAnchorPoint(fixtureBody->anchorPointForShape(name.c_str())); // Đặt cùng 1 điển neo với body } void HelloWorld::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) { Layer::draw(renderer, transform, flags); Director* director = Director::getInstance(); GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION ); director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); world->DrawDebugData(); director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); } void HelloWorld::menuCloseCallback(Ref* pSender) { #if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert"); return; #endif Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif }
Tổng kết
Physics Editor hỗ trợ tạo ra body cho đối tượng có hình dạng phức tạp, nhờ đó:
- Source code được tinh gọn hơn.
- Công việc thiết kế games tiết kiệm được thời gian.
- Việc mô phỏng va chạm giữa các đối tượng chính xác hơn.
- Đặc biệt là công việc thiết kế collision filtering được nhanh chóng.