cocos2d-x中的Box2D物理引擎

     在Cocos2d-x中集成了2个物理引擎,一个是Chipmunk,一个是Box2D。前者是用C语言编写的,文档和例子相对较少;Box2D是用C++写的,并且有比较完善的文档和资料。所以在需要使用物理引擎的时候,大多数开发者会选择Box2D。Box2D是一款用来模拟刚体在物理世界运动的仿真引擎。通过Box2D物理引擎,世界中的物体就可以按照运动定律进行运动。

注:Box2D下的类都是以b2为前缀的,希望不要与你的命名相冲突

1. 首先我们介绍下需要用到的基本概念。

世界(world) :世界代表了一个遵循物理规律的空间,所有的物体都在世界中运动,世界具有创建销毁刚体,创建销毁关节等功能

刚体(body) :一块十分坚硬的物质,它上面的任何两点之间的距离都是完全不变的。刚体具体划分为静态刚体,动态刚体和棱柱刚体(漂浮刚体)

形状(shape):一块严格依附于刚体(body)的 2D 碰撞几何结构(collision geometry)。形状通过关联附加到刚体上,这样刚体就具备了视觉上的外形。

关节(joint):它是一种用于把两个或多个物体固定到一起的约束。Box2D 支持的关节类型有:旋转,棱柱,距离等等。关节可以支持限制(limits)和马达(motors)

关节限制(joint limit):一个关节限制(joint limit)限定了一个关节的运动范围。例如人类的胳膊肘只能做某一范围角度的运动

关节马达(joint motor):一个关节马达能依照关节的自由度来驱动所连接的物体。例如,你可以使用一个马达来驱动一个肘的旋转

2. 我们现在来简单的使用下Box2D,体验一下模拟的物理世界

(1)首先需要创建世界,创建世界需要两个步骤,首先生成重力向量,然后根据重力向量生成世界

bool HelloWorld::init()

{

    if ( !CCLayer::init() )

    {

        return false;

    }

    // b2Vec2带的两个参数是表示x,y方向的重力大小,正负表示方向

    b2Vec2 gravity(0.0f,-10.0f);

    

    // 创建Box2D的世界

    world = new b2World(gravity);

    

    // 设置刚体休眠(当刚体到达边界的时候,停止对刚体的计算,节约CPU)

    world->SetAllowSleeping(true);

    

    // 使用连续的物理检测

    world->SetContinuousPhysics(true);

    

    // 注册碰撞检测的监听

    world->SetContactListener(this);

    

    // 创建漂浮刚体

    addBird(1, 3.33, b2_kinematicBody);

    

    // 创建静态刚体

    addGrass(5.0, 0.5, b2_staticBody);

    

    // 设置屏幕可以触摸

    setTouchEnabled(true);

    

    // 开辟线程回调模拟

    this->scheduleUpdate();

    

    return true;

}

 

 (2)创建非静态刚体

void HelloWorld::addBird(float x,float y,b2BodyType objectType)
{
    /*****   构建刚体的参数(包括位置,类型)   *****/
    
    // 定义刚体用到的精灵
    CCSprite *sprite = CCSprite::create("bird.png");
    addChild(sprite);
    
    // 配置刚体的定义(类似隐形,没有肉体的刚体)
    b2BodyDef def;
    def.position = b2Vec2(x, y); // 设置位置
    def.type = objectType; // 配置刚体的类型,分为三类:静态刚体,漂浮刚体,动态刚体
    if (objectType == b2_kinematicBody) {
        def.linearVelocity = b2Vec2(2, 0); // 两个参数分别代表横向速度和纵向速度
    }
    
    // 定义形状(此处为多边形)
    b2PolygonShape birdShape;
    // 定义边界(世界边界),参数是半宽,半高,因为生成的大小是其传入参数的2倍
    birdShape.SetAsBox(sprite->getContentSize().width / RATIO / 2, sprite->getContentSize().height / RATIO / 2);
    
    // 配置刚体的体积(材料(有肉的实体))
    b2FixtureDef fixtureDef;
    fixtureDef.density = 1; // 密度
    fixtureDef.friction = 0.3; // 摩擦因数
    fixtureDef.restitution = 0.8f; // 反弹效果
    fixtureDef.shape = &birdShape; // 配置刚体的实体
    
    /*****    构建刚体的形体    *****/
    
    // 创建刚体
    birdBody = world->CreateBody(&def);
    
    // 将材料加入到刚体里
    birdBody->CreateFixture(&fixtureDef);
    
    // 绑定精灵到刚体
    birdBody->SetUserData(sprite);
}

 (3) 添加静态刚体

void HelloWorld::addGrass(float x,float y,b2BodyType objectType)
{
    /*****   构建刚体的参数(包括位置,类型)   *****/
    
    // 定义刚体用到的精灵
    CCSprite *sprite = CCSprite::create("grass.png");
    addChild(sprite,1);
    
    // 配置刚体的定义(类似隐形,没有肉体的刚体)
    b2BodyDef def;
    def.position = b2Vec2(x, y); // 设置位置
    def.type = objectType; // 配置刚体的类型,分为三类:静态刚体,漂浮刚体,动态刚体
    
    // 定义形状(此处为多边形)
    b2PolygonShape grassShape;
    // 定义边界(世界边界),参数是半宽,半高,因为生成的大小是其传入参数的2倍
    grassShape.SetAsBox(sprite->getContentSize().width / RATIO / 2 , sprite->getContentSize().height / RATIO / 2);
    
    // 配置刚体的体积(材料(有肉的实体))
    b2FixtureDef fixtureDef;
    fixtureDef.density = 1;
    fixtureDef.friction = 0.3;
    fixtureDef.shape = &grassShape; // 配置刚体的实体
    
    /*****    构建刚体的形体    *****/
    
    // 创建刚体
    grassBody = world->CreateBody(&def);
    
    // 将材料加入到刚体里
    grassBody->CreateFixture(&fixtureDef);
    
    // 绑定精灵到刚体
    grassBody->SetUserData(sprite);
    
}

 (4)驱动模拟世界

// 世界类用于驱动模拟
void HelloWorld::update(float a)
{
    float32 timeStep = 1.0f / 60.f;
    // Step()函数的第一个参数是时间步数,后面两个参数分别是速度阶段和位置阶段
    world->Step(timeStep, 8, 3);
    
    CCSprite *s;
    
    for (b2Body *b = world->GetBodyList(); b; b = b->GetNext()) {
        if (b->GetUserData() != NULL) {
            s = (CCSprite*)b->GetUserData();
// 漂浮刚体的运动模拟 s->setPosition(ccp(b->GetPosition().x * RATIO, b->GetPosition().y * RATIO)); if (s->getPosition().x < -10 || s->getPosition().x > 490 || s->getPosition().y < -10) { this->removeChild(s); world->DestroyBody(b); } } } }

 (5)设置屏幕可以触摸,实现触摸屏幕添加刚体的效果

void HelloWorld::registerWithTouchDispatcher(void)
{
    CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 1, false);
}
bool HelloWorld::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
    CCPoint p = pTouch->getLocation();
    
    addBird(p.x / RATIO, p.y / RATIO, b2_dynamicBody);
    
    return true;
}

 (6)让HelloWorld类继承b2ContactListener,重写BeginContact函数,实现刚体的检测碰撞

// 碰撞检测
void HelloWorld::BeginContact(b2Contact* contact)
{
    if ((contact->GetFixtureA()->GetBody() == grassBody || contact->GetFixtureB()->GetBody() == grassBody)) {
        CCLog("有鸟落在草地上!");
    } else {
        CCLog("鸟与鸟之间发生碰撞");
    }
}

效果如图所示:

原文地址:https://www.cnblogs.com/alenoscar/p/4025161.html