转发,请保持地址:http://blog.youkuaiyun.com/stalendp/article/details/8757660
前面的文章,我介绍了cocosBuilder的简单集成,这篇将介绍怎么使用cocosBuilder开发物理类的游戏。
机器人模型
先介绍结合机器人模型。如下图,机器人有10个图片组合而成,我们先要在cocosBuilder中把各种图片位置摆正确,然后在代码中读取图片之间的位置等信息,创建对应的body,然后用Revolute joint连成一个整体。

在cocosBuilder中创建一个名为robot的ccb,然后组合组成如下的情况:

调整各个模块的anchor(Chest除外),这些anchor点将被程序读取,作为joint点。然后导出ccbi文件。
在xcode中导入robot.ccbi,然后创建一个robot类,继承于CcbBase类(参见上一片文章《
[cocos2dx开发技巧2]工具CocosBuilder的使用--集成》);覆盖onAssignCCBMemberVariable方法,如下:
bool onAssignCCBMemberVariable(cocos2d::CCObject * pTarget, const char * pMemberVariableName, cocos2d::CCNode * pNode) {
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "head", CCSprite *, this->head);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "chess", CCSprite *, this->chess);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "leftArm", CCSprite *, this->leftArm);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "leftHand", CCSprite *, this->leftHand);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "leftLeg", CCSprite *, this->leftLeg);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "leftFoot", CCSprite *, this->leftFoot);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "rightArm", CCSprite *, this->rightArm);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "rightHand", CCSprite *, this->rightHand);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "rightLeg", CCSprite *, this->rightLeg);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "rightFoot", CCSprite *, this->rightFoot);
return NULL;
}
覆盖onNodeLoaded方法如下:
void onNodeLoaded(cocos2d::CCNode * pNode, cocos2d::extension::CCNodeLoader * pNodeLoader) {
CCSpriteBatchNode* robot = CCSpriteBatchNode::createWithTexture(head->getTexture()); //这里将使用SpriteBatchNode来提高性能
addChild(robot);
MyPhysicsSprite::attach(robot, head);
MyPhysicsSprite::attach(robot, chess);
MyPhysicsSprite::attach(robot, leftArm);
MyPhysicsSprite::attach(robot, leftHand);
MyPhysicsSprite::attach(robot, leftLeg);
MyPhysicsSprite::attach(robot, leftFoot);
MyPhysicsSprite::attach(robot, rightArm);
MyPhysicsSprite::attach(robot, rightHand);
MyPhysicsSprite::attach(robot, rightLeg);
MyPhysicsSprite::attach(robot, rightFoot);
createJoin(chess, head, head->getAnchorPoint());
createJoin(chess, leftArm, leftArm->getAnchorPoint());
createJoin(leftArm, leftHand, leftHand->getAnchorPoint());
createJoin(chess, rightArm, rightArm->getAnchorPoint());
createJoin(chess, leftLeg, leftLeg->getAnchorPoint());
createJoin(chess, rightLeg, rightLeg->getAnchorPoint());
createJoin(rightArm, rightHand, rightHand->getAnchorPoint());
createJoin(leftLeg, leftFoot, leftFoot->getAnchorPoint());
createJoin(rightLeg, rightFoot, rightFoot->getAnchorPoint());
}
关于Join的创建,代码如下:
void createJoin(CCSprite* a, CCSprite* b, CCPoint anchor) {
b2RevoluteJointDef rjd;
rjd.maxMotorTorque = 50.0f;
rjd.enableMotor = false;
rjd.collideConnected=false;
rjd.motorSpeed= 20*DEGTORAD;
rjd.Initialize(((MyPhysicsSprite*)a)->getBody(), ((MyPhysicsSprite*)b)->getBody(), ((MyPhysicsSprite*)b)->getBody()->GetPosition());
MyWorld::getWorld()->CreateJoint(&rjd);
}
场景的建立
这里将创建一个robot玩耍的场地。新建一个ccb文件,布置场景如下:

导出ccbi,在xcode中导入ccbi,接着创建一个playground类,关联相关属性如下:
virtual bool onAssignCCBMemberVariable(cocos2d::CCObject * pTarget, const char * pMemberVariableName, cocos2d::CCNode * pNode) {
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "apple", CCSprite *, this->apple);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "ground", CCSprite *, this->ground);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "wleft", CCSprite *, this->wleft);
CCB_MEMBERVARIABLEASSIGNER_GLUE(this, "wright", CCSprite *, this->wright);
return false;
}
创建box2d物体:
virtual void onNodeLoaded(cocos2d::CCNode * pNode, cocos2d::extension::CCNodeLoader * pNodeLoader) {
GB2ShapeCache::sharedGB2ShapeCache()->addShapesWithFile("apple_pe.plist");
MyWorld::init();
MyPhysicsSprite::attach(this, apple, false, "apple");
MyPhysicsSprite::attach(this, ground, true);
MyPhysicsSprite::attach(this, wleft, true);
MyPhysicsSprite::attach(this, wright, true);
scheduleUpdate();
robot = (Robot*)RobotLoader::loadCcbi();
addChild(robot);
CCSize size = CCDirector::sharedDirector()->getVisibleSize();
robot->setPhysicPos(ccp(size.width/2, size.height/2));
//robot->setPosition(ccp(size.width/2, size.height/2));
}
对物体施加作用
接着就是实现Play按钮的功能了,在响应Play按钮的点击时,为apple和robot的chess施加一个向上的impluse,使得它们运动起来。
在cocosBuilder中选择Play按钮,在右侧的CCMenuItem中为selector命名为onPlay,并选择target为Document root。具体如下:

在Playground类中,实现如下代码:
virtual cocos2d::SEL_MenuHandler onResolveCCBCCMenuItemSelector(cocos2d::CCObject * pTarget, const char * pSelectorName) {
CCB_SELECTORRESOLVER_CCMENUITEM_GLUE(this, "onPlay", Playground::btnPlay);
return NULL;
}
实现btnPlay功能:
void btnPlay(cocos2d::CCObject *pSender) {
// 对apple施加向上的大小为30的impulse
b2Body* body = dynamic_cast<MyPhysicsSprite*>(apple)->getBody();
body->ApplyLinearImpulse(b2Vec2(0, 30), body->GetWorldCenter());
// 对robot的chess施加b2Vec2(10,20)的impulse
b2Body* bb = ((MyPhysicsSprite*)robot->chess)->getBody();
bb->ApplyLinearImpulse(b2Vec2(10,20), b2Vec2(bb->GetWorldCenter().x + 0.5, bb->GetWorldCenter().y));
}
ok,至此box2d就集成完毕了。效果请参考《 [cocos2dx开发技巧1]工具CocosBuilder的使用--demo介绍》
另外附一些代码:
这个MyPhysicsSprite可以使用CCPhysicsSprite类。
#ifndef ShootTheApple_PhysicsSprite_h
#define ShootTheApple_PhysicsSprite_h
#include "cocos2d.h"
#include "cocos-ext.h"
#include "Box2D.h"
#include "MyWorld.h"
#include "GB2ShapeCache-x.h"
#define PTM_RATIO 32
USING_NS_CC;
USING_NS_CC_EXT;
#define DEGTORAD 0.0174532925199432957f
#define RADTODEG 57.295779513082320876f
class MyPhysicsSprite : public cocos2d::CCSprite
{
private:
b2Body* m_pBody; // strong ref
public:
static MyPhysicsSprite* attach(CCNode* parent, CCSprite*& sprite, bool isStatic = false, const char* peName=NULL) {
MyPhysicsSprite *pobSprite = new MyPhysicsSprite(sprite, isStatic, peName);
CCSpriteFrame *pSpriteFrame = sprite->displayFrame();
CCPoint anchor = sprite->getAnchorPoint();
if (pSpriteFrame && pobSprite && pobSprite->initWithSpriteFrame(pSpriteFrame))
{
pobSprite->autorelease();
parent->addChild(pobSprite);
cocos2d::CCLog(cocos2d::CCString::createWithFormat("anchor<%.2f, %.2f>", anchor.x, anchor.y)->getCString());
sprite->autorelease(); // no need anymore
sprite->setVisible(false);
sprite = pobSprite;
sprite->setAnchorPoint(anchor);
return pobSprite;
}
CC_SAFE_DELETE(pobSprite);
return NULL;
}
b2Body* getBody() {
return m_pBody;
}
MyPhysicsSprite(CCSprite* sprite, bool isStatic, const char* peName): m_pBody(NULL) {
init(sprite, isStatic, peName);
}
virtual void init(CCSprite* sprite, bool isStatic, const char* peName) {
b2World* world = MyWorld::getWorld();
b2Body *body = NULL;
CCPoint p = sprite->getPosition();
CCSize size = sprite->getContentSize();
CCPoint anchor = sprite->getAnchorPoint();
float angle = -sprite->getRotation()*DEGTORAD;
b2BodyDef bodyDef;
bodyDef.type = isStatic ? b2_staticBody : b2_dynamicBody;
bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
bodyDef.angle = angle;
body = world->CreateBody(&bodyDef);
// if(peName!=NULL) {
// GB2ShapeCache *sc = GB2ShapeCache::sharedGB2ShapeCache();
// sc->addFixturesToBody(body, peName);
// setAnchorPoint(sc->anchorPointForShape(peName));
// } else {
// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
b2Vec2 center((0.5-anchor.x)*size.width/PTM_RATIO, (0.5-anchor.y) * size.height/PTM_RATIO);
dynamicBox.SetAsBox(size.width/PTM_RATIO/2, size.height/PTM_RATIO/2, center, 0);//These are mid points for our 1m box
// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.1f;
// fixtureDef.restitution = 0.5f;
body->CreateFixture(&fixtureDef);
// }
this->m_pBody = body;
}
virtual bool isDirty(void) {
return m_pBody!=NULL && m_pBody->IsAwake(); // 只有物体在运动中,才需要更新显示
}
void setPhysicPos(CCPoint pos) {
m_pBody->SetTransform(b2Vec2(pos.x/PTM_RATIO, pos.y/PTM_RATIO), 0);
}
void physicsTrans(CCPoint pos) {
if(m_pBody==NULL)
return;
b2Vec2 cur = m_pBody->GetPosition();
m_pBody->SetTransform(b2Vec2(cur.x + pos.x/PTM_RATIO, cur.y + pos.y/PTM_RATIO), 0);
}
virtual cocos2d::CCAffineTransform nodeToParentTransform(void) {
b2Vec2 pos = m_pBody->GetPosition();
float x = pos.x * PTM_RATIO;
float y = pos.y * PTM_RATIO;
if ( isIgnoreAnchorPointForPosition() ) {
x += m_obAnchorPointInPoints.x;
y += m_obAnchorPointInPoints.y;
}
// Make matrix
float radians = m_pBody->GetAngle();
float c = cosf(radians);
float s = sinf(radians);
if( ! m_obAnchorPointInPoints.equals(CCPointZero) ){
x += c*-m_obAnchorPointInPoints.x + -s*-m_obAnchorPointInPoints.y;
y += s*-m_obAnchorPointInPoints.x + c*-m_obAnchorPointInPoints.y;
}
// Rot, Translate Matrix
m_sTransform = CCAffineTransformMake( c, s,
-s, c,
x, y );
return m_sTransform;
}
};
#endif
#ifndef ShootTheApple_MyWorld_h
#define ShootTheApple_MyWorld_h
#include "Box2D.h"
#define VELOCITY_ITER 8
#define POSITION_ITER 1
class MyWorld {
private:
static b2World* world;
public:
static void init() {
if (world!=NULL) {
return;
}
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
world = new b2World(gravity);
// Do we want to let bodies sleep?
world->SetAllowSleeping(true);
world->SetContinuousPhysics(true);
}
static b2World* getWorld() {
if(world==NULL) {
init();
}
return world;
}
static void update(float dt) {
getWorld()->Step(dt, VELOCITY_ITER, POSITION_ITER);
}
};
b2World* MyWorld::world=NULL;
#endif