衆所周知,Box2D中的剛體形狀如果比較簡單,我們可以使用Box2D中的幾個形狀子類生成,但是如果我們遊戲中的剛體的形狀比較複雜,那我們需要使用第三方工具生成,而現在Box2D的第三方工具很少,目前所知道的隻有兩個:一個是在Mac系統下使用的
VertexHelper工具,另一個是Windows系統下使用的PhysicsEditor工具,我們這裡就來講講PhysicsEditor工具導出的plist檔案在Cococs2d-x中的使用方法。
注意:目前最新版本的PhysicsEditor工具的安裝目錄下面的Demo裡面的GB2ShapeCache-x解析類已經不支援Cocos2d-x 2.x版本,但是我們可以在它原來的基礎上進行修改,修改後的GB2ShapeCache-x解析類經過測試,對Cocos2d-x 2.x已經全部支援。下面給出修改後的GB2ShapeCache-x解析類檔案:
#ifndef GB2ShapeCache_x_h
#define GB2ShapeCache_x_h
#include "cocos2d.h"
#include <map>
class BodyDef;
class b2Body;
NS_CC_BEGIN
class GB2ShapeCache
{
public:
static GB2ShapeCache* sharedGB2ShapeCache(void);
public:
bool init();
void addShapesWithFile(const std::string &plist);
void addFixturesToBody(b2Body *body, const std::string &shape);
cocos2d::CCPoint anchorPointForShape(const std::string &shape);
void reset();
float getPtmRatio() { return ptmRatio; }
~GB2ShapeCache() {}
private:
std::map<std::string, BodyDef *> shapeObjects;
GB2ShapeCache(void) {}
float ptmRatio;
};
NS_CC_END
#endif
#include "GB2ShapeCache-x.h"
#include "Box2D/Box2D.h"
#include "cocoa/CCNS.h"
using namespace cocos2d;
class FixtureDef
{
public:
FixtureDef()
:next(NULL)
{
}
~FixtureDef()
{
delete next;
delete fixture.shape;
}
FixtureDef *next;
b2FixtureDef fixture;
int callbackData;
};
class BodyDef
{
public:
BodyDef()
:fixtures(NULL)
{
}
~BodyDef()
{
if (fixtures)
delete fixtures;
}
FixtureDef *fixtures;
CCPoint anchorPoint;
};
static GB2ShapeCache *_sharedGB2ShapeCache = NULL;
GB2ShapeCache* GB2ShapeCache::sharedGB2ShapeCache(void)
{
if (!_sharedGB2ShapeCache)
{
_sharedGB2ShapeCache = new GB2ShapeCache();
_sharedGB2ShapeCache->init();
}
return _sharedGB2ShapeCache;
}
bool GB2ShapeCache::init()
{
return true;
}
void GB2ShapeCache::reset()
{
std::map<std::string, BodyDef *>::iterator iter;
for (iter = shapeObjects.begin(); iter != shapeObjects.end(); ++iter)
{
delete iter->second;
}
shapeObjects.clear();
}
void GB2ShapeCache::addFixturesToBody(b2Body *body, const std::string &shape)
{
std::map<std::string, BodyDef *>::iterator pos = shapeObjects.find(shape);
assert(pos != shapeObjects.end());
BodyDef *so = (*pos).second;
FixtureDef *fix = so->fixtures;
while (fix)
{
body->CreateFixture(&fix->fixture);
fix = fix->next;
}
}
cocos2d::CCPoint GB2ShapeCache::anchorPointForShape(const std::string &shape)
{
std::map<std::string, BodyDef *>::iterator pos = shapeObjects.find(shape);
assert(pos != shapeObjects.end());
BodyDef *bd = (*pos).second;
return bd->anchorPoint;
}
typedef CCDictionary ObjectDict;
void GB2ShapeCache::addShapesWithFile(const std::string &plist)
{
string fullName = CCFileUtils::sharedFileUtils()->fullPathForFilename(plist.c_str());
CCDictionary* dict = CCDictionary::createWithContentsOfFile(fullName.c_str());
CCLOG(fullName.c_str());
CCAssert(dict != NULL, "Shape-file not found"); // not triggered - cocos2dx delivers empty dict if non was found
CCAssert(dict->count() != 0, "plist file empty or not existing");
CCDictionary* metadataDict = (CCDictionary*)dict->objectForKey("metadata");
int format = metadataDict->valueForKey("format")->intValue();
ptmRatio = metadataDict->valueForKey("ptm_ratio")->floatValue();
CCAssert(format == 1, "Format not supported");
CCDictionary* bodyDict = (CCDictionary*)dict->objectForKey("bodies");
b2Vec2 vertices[b2_maxPolygonVertices];
CCLOG("bodydict count %d ", bodyDict->count());
CCDictElement* pElement = NULL;
CCDICT_FOREACH(bodyDict, pElement)
{
CCDictionary* bodyData = (CCDictionary*)pElement->getObject();
CCLOG("body key %s -> bodyData count %d", pElement->getStrKey(), bodyData->count());
BodyDef* bodyDef = new BodyDef();
shapeObjects[pElement->getStrKey()] = bodyDef;
CCLOG("anchorpoint %s", bodyData->valueForKey("anchorpoint")->getCString());
bodyDef->anchorPoint = CCPointFromString(bodyData->valueForKey("anchorpoint")->getCString());
CCArray* fixtureList = (CCArray*)(bodyData->objectForKey("fixtures"));
FixtureDef **nextFixtureDef = &(bodyDef->fixtures);
CCObject* pObj = NULL;
CCARRAY_FOREACH(fixtureList, pObj)
{
b2FixtureDef basicData;
CCDictionary* fixtureData = (CCDictionary*)pObj;
basicData.filter.categoryBits = fixtureData->valueForKey("filter_categoryBits")->intValue();
basicData.filter.maskBits = fixtureData->valueForKey("filter_maskBits")->intValue();
basicData.filter.groupIndex = fixtureData->valueForKey("filter_groupIndex")->intValue();
basicData.friction = fixtureData->valueForKey("friction")->floatValue();
basicData.density = fixtureData->valueForKey("density")->floatValue();
basicData.restitution = fixtureData->valueForKey("restitution")->floatValue();
basicData.isSensor = fixtureData->valueForKey("isSensor")->boolValue();
int cb = fixtureData->valueForKey("userdataCbValue")->intValue();
int callbackData = cb ? cb : 0;
std::string fixtureType = fixtureData->valueForKey("fixture_type")->m_sString;
if (fixtureType == "POLYGON")
{
CCArray* polygonsArray = (CCArray*)fixtureData->objectForKey("polygons");
CCObject* pObject;
CCARRAY_FOREACH(polygonsArray, pObject)
{
FixtureDef *fix = new FixtureDef();
fix->fixture = basicData; // copy basic data
fix->callbackData = callbackData;
b2PolygonShape *polyshape = new b2PolygonShape();
int vindex = 0;
CCArray* polygonArray = (CCArray*)pObject;
CCObject* pObject;
CCAssert(polygonsArray->count(), "polygonsArray = 0!");
CCARRAY_FOREACH(polygonArray, pObject)
{
CCPoint offset = CCPointFromString(((CCString *)pObject)->getCString());
vertices[vindex].x = (offset.x / ptmRatio);
vertices[vindex].y = (offset.y / ptmRatio);
vindex++;
}
polyshape->Set(vertices, vindex);
fix->fixture.shape = polyshape;
// create a list
*nextFixtureDef = fix;
nextFixtureDef = &(fix->next);
}
}
else if (fixtureType == "CIRCLE")
{
FixtureDef *fix = new FixtureDef();
fix->fixture = basicData; // copy basic data
fix->callbackData = callbackData;
CCDictionary *circleData = (CCDictionary *)fixtureData->objectForKey("circle");
b2CircleShape *circleShape = new b2CircleShape();
circleShape->m_radius = circleData->valueForKey("radius")->floatValue() / ptmRatio;
CCPoint p = CCPointFromString(circleData->valueForKey("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");
}
}
}
}
針對PhysicsEditor工具的使用,很簡單,可以自己查找資料,這裡就不再累述,我們把導出來的plist檔案拷貝到Resource目錄下就可以了,下面給出使用代碼:
#ifndef __HELLO_WORLD_H__
#define __HELLO_WORLD_H__
#include "cocos2d.h"
#include "cocos-ext.h"
#include "Box2D/Box2D.h"
USING_NS_CC;
USING_NS_CC_EXT;
class HelloWorld : public cocos2d::CCLayer
{
public:
HelloWorld();
virtual ~HelloWorld();
static cocos2d::CCScene* scene();
virtual void onEnter();
virtual void onExit();
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual bool init();
virtual void update(float dt);
CREATE_FUNC(HelloWorld);
private:
b2World* world;
private:
void addNewSpriteWithCoords(cocos2d::CCPoint p);
};
#endif
//
// HelloWorldScene.cpp
// Demo
//
// Created by Andreas L枚w on 11.01.12.
// Copyright codeandweb.de 2012. All rights reserved.
//
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
#include "GB2ShapeCache-x.h"
#define PTM_RATIO 30
HelloWorld::HelloWorld()
{
}
HelloWorld::~HelloWorld()
{
if (world)
{
delete world;
world = NULL;
}
}
bool HelloWorld::init()
{
if (!CCLayer::init())
{
return false;
}
// 載入實體形狀
GB2ShapeCache::sharedGB2ShapeCache()->addShapesWithFile("Box2D.plist");
// 開啟多點觸控
//this->setTouchEnabled(true);
// 開啟重力加速器
//this->setAccelerometerEnabled(true);
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
// 定義重力方向
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
// 剛體是否睡眠
bool doSleep = true;
bool continuous = true;
world = new b2World(gravity);
world->SetAllowSleeping(doSleep);
world->SetContinuousPhysics(continuous);
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(winSize.width / 2 / PTM_RATIO, winSize.height / 2 / PTM_RATIO);
b2Body* groundBody = world->CreateBody(&groundBodyDef);
// 定義一個地面盒形狀
b2PolygonShape groundBox;
groundBox.SetAsBox(winSize.width / 2 / PTM_RATIO, 0, b2Vec2(0, -winSize.height / 2 / PTM_RATIO), 0);
b2FixtureDef fixtureDef;
fixtureDef.shape = &groundBox;
fixtureDef.density = 1; //密度
fixtureDef.friction = 0.5f; //摩擦因數
fixtureDef.restitution = 0.4f; //彈性因數
// 底部
groundBody->CreateFixture(&fixtureDef);
// 頂部
groundBox.SetAsBox(winSize.width / 2 / PTM_RATIO, 0, b2Vec2(0, winSize.height / 2 / PTM_RATIO), 0);
groundBody->CreateFixture(&groundBox, 0);
// 左邊
groundBox.SetAsBox(0, winSize.height / 2 / PTM_RATIO, b2Vec2(-winSize.width / 2 / PTM_RATIO, 0), 0);
groundBody->CreateFixture(&groundBox, 0);
// 右邊
groundBox.SetAsBox(0, winSize.height / 2 / PTM_RATIO, b2Vec2(winSize.width / 2 / PTM_RATIO, 0), 0);
groundBody->CreateFixture(&groundBox, 0);
// 設定精靈
CCLabelTTF *label = CCLabelTTF::create("螢幕頂端", "Arial", 32);
this->addChild(label, 0);
label->setColor(ccc3(0, 0, 255));
label->setPosition(CCPointMake(winSize.width / 2, winSize.height - 50));
this->addNewSpriteWithCoords(CCPointMake(winSize.width / 2, winSize.height / 2));
this->scheduleUpdate();
return true;
}
string names[] = {
"BoxA"
/*"hotdog",
"drink",
"icecream",
"icecream2",
"icecream3",
"hamburger",
"orange"*/
};
void HelloWorld::addNewSpriteWithCoords(CCPoint p)
{
string name = names[0];//names[rand() % 7];
CCLOG("name:%s", name.c_str());
CCSprite *sprite = CCSprite::create((name + ".png").c_str());
sprite->setPosition(p);
this->addChild(sprite);
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);
// 剛體添加夾具
GB2ShapeCache::sharedGB2ShapeCache()->addFixturesToBody(body, name.c_str());
sprite->setAnchorPoint(GB2ShapeCache::sharedGB2ShapeCache()->anchorPointForShape(name.c_str()));
}
void HelloWorld::update(float dt)
{
int velocityIterations = 8;
int positionIterations = 3;
world->Step(dt, velocityIterations, positionIterations);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL)
{
CCSprite* myActor = (CCSprite*)b->GetUserData();
myActor->setPosition(CCPointMake(b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO));
myActor->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));
}
}
}
void HelloWorld::onEnter()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, -128, true);
CCLayer::onEnter();
}
void HelloWorld::onExit()
{
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
CCLayer::onExit();
}
bool HelloWorld::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
return true;
}
void HelloWorld::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
{
CCPoint pt = pTouch->getLocation();
this->addNewSpriteWithCoords(pt);
}
//void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
//{
// CCLOG("HelloWorld::ccTouchesBegan");
// CCSetIterator it;
// CCTouch *touch;
//
// for (it = pTouches->begin(); it != pTouches->end(); it++)
// {
// touch = (CCTouch*)(*it);
//
// if (!touch)
// break;
//
// CCPoint location = touch->getLocation();
// this->addNewSpriteWithCoords(location);
// }
//}
CCScene* HelloWorld::scene()
{
CCScene *scene = CCScene::create();
HelloWorld *layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}