转发,请保持地址:http://blog.csdn.net/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