首页 > 代码库 > Cocos2d-x 系列八之Box2d入门

Cocos2d-x 系列八之Box2d入门

  既然已有了cocos2d-x,为什么还要Box2d呢,是因为cocos2d-x作为一个图像引擎,只是用于显示图像,图像之间可以任意的重合,如果想要做到类物理学的碰撞等运动效果,就需要用到Box2d这个物理引擎用来模仿物理世界中的物体;

  本讲主要简单讲述如何创建动态物体,静态物体,漂浮物体,以及它们与图像的绑定; 

下面直接通过一个例子来看三种物体的创建方法;

  首先需要说明的一点是:在Box2d中,使用的单位是米,而不是像素,所以,在进行位置转换的时候,需要按比例缩放,Box2d中,比较理想的距离大小是10米,所以,得对屏幕宽高进行一定的比例缩放(如下面例子中定义的RADIO,就是我定义的比例);

HelloWorldScene.cpp

#include "HelloWorldScene.h"

#define RADIO 80

Scene* HelloWorld::createScene()
{
    // ‘scene‘ is an autorelease object
    auto scene = Scene::create();

    // ‘layer‘ is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }

    world = new b2World(b2Vec2(0,-10)); //x 方向加速度为0,y方向加速度为10 
    world->SetContactListener(this); // 添加物体撞击事件监听

    addGround();
    addRect();

    scheduleUpdate(); // 开始执行handler,第帧执行一次update方法

    return true;
}

void HelloWorld::update(float dt){
    world->Step(dt,8,3); // 三个参数分别表示当前帧和最后一帧之间的时间间隔,由于update方法是程序每一帧执行一次,所以直接传入帧频dt;
                //第二个参数表示速度迭代次数,官方建议8;
                //第三个参数表示位置迭代次数,官方建议3;
Sprite
*s; for(b2Body *b = world->GetBodyList();b;b= b->GetNext()){ if(b->GetUserData()){ s = (Sprite*)b->GetUserData(); s->setPosition(b->GetPosition().x * RADIO ,b->GetPosition().y *RADIO); // 更新物体的位置 } } } void HelloWorld::BeginContact(b2Contact* contact){ // b2ContactListener物体发生碰撞时的回调接口 log("contact?"); if(contact->GetFixtureA()->GetBody() == ground ||contact->GetFixtureB()->GetBody() == ground){ log("yeah,contacted!"); } } void HelloWorld::addRect(){ // 添加一个动态的Rect,并且绑定一个Sprite; b2BodyDef def; def.type = b2_dynamicBody; // 指定type为动态物体 @1 def.position = b2Vec2(3,1); // 起始位置(3,1) def.linearVelocity = b2Vec2(0,10); // (初始)线性速度,如果type为漂浮的,则物体会没这个方向做匀速运动,如果为动态,则会进行预先设定的加速度进行速度变换; b2PolygonShape shape; shape.SetAsBox(0.5,0.5); // 设置一个Shape,两个参数分别表示左半边和上半边的大小,所以,这里两个0.5其实就定义了一个1x1的块 b2FixtureDef fixtureDef; fixtureDef.density = 1; // 指定物体密度 fixtureDef.friction = 0.3; // 指定物体摩擦 fixtureDef.shape = &shape; // 指定物体的shape b2Body *body = world->CreateBody(&def); auto s = Sprite::create(); s->setTextureRect(Rect(0 , 0, 30, 30)); // 设置sprite所显示的区域 addChild(s); body->CreateFixture(&fixtureDef); // 设置FixtureDef之后,两个物体之间就会有碰撞的效果了,如果没有碰撞的效果,则物体会一直沿某一个方向不停运动; body->SetUserData(s); // 将Sprite绑定到物体上 } void HelloWorld::addGround(){ //添加一个静态物体,在为里为添加到底部,阻止上一步中创建的自由落体的物体 b2BodyDef def; def.type = b2_staticBody; def.position = b2Vec2(0, 0); b2PolygonShape shape; shape.SetAsBox(400/RADIO, 10 / RADIO); // 设置物体大小 ground = world->CreateBody(&def); auto s = Sprite::create(); s->setTextureRect(Rect(0,0,1600,40)); b2FixtureDef fixtureDef; fixtureDef.density = 0.5; fixtureDef.friction = 0.3; fixtureDef.shape = &shape; addChild(s); ground->CreateFixture(&fixtureDef); ground->SetUserData(&def); }

HelloWorldScene.h

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include <Box2D/Box2D.h>

USING_NS_CC;

class HelloWorld : public Layer,public b2ContactListener
{
private :
    b2World *world;
    b2Body *ground;
public:
    // there‘s no ‘id‘ in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();

    // Here‘s a difference. Method ‘init‘ in cocos2d-x returns bool, instead of returning ‘id‘ in cocos2d-iphone
    virtual bool init();  

    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);

    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);

    virtual void update(float dt);

    void addRect();
    void addGround();

    /// Called when two fixtures begin to touch.
    virtual void BeginContact(b2Contact* contact) ;

};

#endif // __HELLOWORLD_SCENE_H__

@1: 这里的type有三种类型如下

  • b2_staticBody(静态)
  • b2_kinematicBody(漂浮,可以运动,但是速度恒定)
  • b2_dynamicBody(动态,速度在变化)