[cocos2dx开发技巧3]工具CocosBuilder的使用--Box2d

时间:2023-02-08 08:00:19

转发,请保持地址:http://blog.csdn.net/stalendp/article/details/8757660

前面的文章,我介绍了cocosBuilder的简单集成,这篇将介绍怎么使用cocosBuilder开发物理类的游戏。

机器人模型

先介绍结合机器人模型。如下图,机器人有10个图片组合而成,我们先要在cocosBuilder中把各种图片位置摆正确,然后在代码中读取图片之间的位置等信息,创建对应的body,然后用Revolute joint连成一个整体。
[cocos2dx开发技巧3]工具CocosBuilder的使用--Box2d

在cocosBuilder中创建一个名为robot的ccb,然后组合组成如下的情况:
[cocos2dx开发技巧3]工具CocosBuilder的使用--Box2d
调整各个模块的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文件,布置场景如下:
[cocos2dx开发技巧3]工具CocosBuilder的使用--Box2d
导出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。具体如下:
[cocos2dx开发技巧3]工具CocosBuilder的使用--Box2d
在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