cocos2d-x + Box2d PhysicsEditorで作ったデータを読み込む
cocos2d-xでBox2dを使う - おかひろの雑記 で、Cocos2d-xでBox2dを使う設定をしましたが、
実際にゲームで使いたい物理オブジェクトの形は複雑なことが多いので、
物理体の座標を個別に設定するのは相当難しいと思います。
なので、PhysicsEditor(https://www.codeandweb.com/physicseditor)を使って物理体の定義を行い、そのデータをCocos2d-xで読み込むようにしてみます。
Cocos2d-x側でPhysicsEditorのデータを読み込むには、GitHub - tks2shimizu/GB2ShapeCache-x-for-cocos2d-x-3.x: GB2ShapeCache-x for cocos2d-x 3.xを使うと良いと思います。
今回使用したバージョン
- Cocos2d-x 3.9
- PhysicsEditor 1.5.2
動作するサンプルはこちら。 https://github.com/okahiro/Cocos2dxBox2d
準備
GB2ShapeCache-xをダウンロードし、プロジェクトに追加します。
そのままではコンパイルが通らないので、includeの部分を修正します。
修正箇所
#include "cocoa/CCNS.h" ↓ #include "CCNS.h"
また、addShapesWithFileメソッドでメモリリークしてるっぽいので、最後にdelete dict;を追加します。
(間違っていたらすみません)
.... } else if (fixtureType == "CIRCLE") { auto fix = new FixtureDef(); fix->fixture = basicData; // copy basic data fix->callbackData = callbackData; auto circleData = (Dictionary *)fixtureData->objectForKey("circle"); auto circleShape = new b2CircleShape(); circleShape->m_radius = static_cast<String *>(circleData->objectForKey("radius"))->floatValue() / ptmRatio; auto p = PointFromString(static_cast<String *>(circleData->objectForKey("position"))->getCString()); circleShape->m_p = b2Vec2(p.x / ptmRatio, p.y / ptmRatio); fix->fixture.shape = circleShape; // create a list *nextFixtureDef = fix; nextFixtureDef = &(fix->next); } else { CCAssert(0, "Unknown fixtureType"); } // add the body element to the hash shapeObjects[bodyName->getCString()] = bodyDef; } } delete dict; // これを追加 }
plistファイルの作成
PhysicsEditorを起動し、物理体を定義します。
サンプルで4つの物理体を作成しました。
"circle"、"cross"、"square"、"triangle"などの名前は後で使うので、きちんとした名前をつけておく必要があります。
Publishでplistを作成します。
plistファイルの読み込み
initメソッドでplistの読み込みを行います。
// 初期化 bool Box2dAndPEScene::init() { if ( !Layer::init() ) { return false; } Size winSize = Director::getInstance()->getWinSize(); // 物理設定 b2Vec2 gravity; gravity.Set(0.0f, -25.0f); // 重力の値は動きを見ながら調整 // World作成 _world = new b2World(gravity); _world->SetAllowSleeping(true); _world->SetContinuousPhysics(true); // PhysicsEditorから出力したplistを読み込み gbox2d::GB2ShapeCache::getInstance()->addShapesWithFile("res/shapes.plist"); // 画面をタップしたら auto listenerForSprite = EventListenerTouchOneByOne::create(); listenerForSprite->setSwallowTouches(true); listenerForSprite->onTouchBegan = [=](Touch* touch, Event* event) { Vec2 pos = touch->getLocation(); // ブロックを作成 this->createBlock(pos.x, pos.y); return true; }; Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listenerForSprite, this); // 地面を作成 this->createGround(); // updateメソッドを開始 this->scheduleUpdate(); return true; }
物理体作成
大分シンプルになりました。
PhysicsEditor上でつけた名前を指定して物理体を作成します。
// ブロックを作成する void Box2dAndPEScene::createBlock(int x,int y) { _blockNo++; // ブロックの名前(PhysicsEditer上でつけた名前) std::string blockName = ""; // Noによってブロックを変える switch(_blockNo % 4) { case 0: blockName = "circle"; break; case 1: blockName = "cross"; break; case 2: blockName = "square"; break; case 3: blockName = "triangle"; break; default: blockName = "circle"; break; } // Sprite Sprite *block = Sprite::create(StringUtils::format("res/%s.png",blockName.c_str())); block->setTag(_blockNo); // ブロックに番号をつける this->addChild(block); b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; bodyDef.position.Set(x / PTM_RATIO,y / PTM_RATIO); b2Body *body = this->_world->CreateBody(&bodyDef); gbox2d::GB2ShapeCache::getInstance()->addFixturesToBody(body, blockName); // bodyに、plist上の指定の物理体情報でFixture作成 body->SetUserData(block); // AnchorPointを取得して設定 block->setAnchorPoint(gbox2d::GB2ShapeCache::getInstance()->anchorPointForShape(blockName)); CCLOG("■ブロック%dを作成しました。",_blockNo); }
これで、画面をタップすると十字、正方形、三角形、円のオブジェクトが順番にでてくるようになりました。