Cocos2d-x教程(30)-3.x版本物理引擎的使用

时间:2022-08-09 04:56:37

欢迎加入Cocos2d-x 交流群:193411763

转载时请注明原文出处 : http://blog.csdn.net/u012945598/article/details/38417333



在Cocos2d-x 2.x的版本中,开发者可以直接使用Box2d或chipmunk进行物理世界的模拟。

在第17篇教程(http://blog.csdn.net/u012945598/article/details/17787243)我们也曾经介绍过Box2d物理引擎的使用,但是Box2d中的api对于刚接触物理引擎的开发者来说仍然显得很复杂。

在Cocos2d-x 3.0版本中,Cocos2d-x对物理引擎的进行了封装,使其融入2d-x中,避免了开发者直接使用物理引擎较为复杂的接口,使物理引擎的使用变得十分简单(目前3.0版本封装的是chipmunk物理引擎,该引擎在功能上不及Box2d,但用法简单)。


在2.x版本中若要模拟一个物理世界并使精灵受到物理效果的影响大体需要如下几步:
(1) 创建一个物理世界。

(2) 创建物体(Body)。

(3) 创建夹具/框架(Fixture)。

(4) 创建关节(Joints)。

(5) 第四部可根据需要进行取舍,最后设置用户交互。

这样在2.x版本中就可以模拟出来一个物理世界,碰撞检测可通过继承b2ContactListener重写BeginContact函数来实现。


在3.x版本中,既然api已经被封装到引擎内的组件,使用时便无需那么麻烦了,但仍然需要几个步骤方可使用物理引擎。

(1) 使用Scene类的提供的工厂方法createWithPhysics()创建一个带有物理世界的场景。

(2) 使用PhysicsBody类创建物体,同时也对物体的夹具及形状进行了绑定。

(3) 使用Node类setPhysicsBody()方法将节点与物体绑定,即设置用户交互(Sprite,ImageView等均属于Node派生类,可访问此方法)。

(4) 若有需要,使用PhysicsJointLimit类创建关节,将物体进行连接,将关节添加到world即可。

(5) 碰撞检测事件由EventListenerPhysicsContact监听,创建实例对象后设置相应的回调函数即可。


尽管步骤看上去并未减少,但实际上在3.x版本中使用物理引擎代码的复杂度要比2.x小的多。

下面写一个简单的Demo来模拟这个过程。


首先创建一个能够支持物理世界的场景。


Scene* HelloWorld::createScene()
{
    /*  这里的HelloWorld类继承于Scene并非Layer  */
    HelloWorld * pRet = new HelloWorld();
    /*  相当于 auto scene = Scene::createWithPhysics()  */
    if (pRet && pRet->initWithPhysics()) {
        pRet->init();
        /* 设置物理世界的重力,y轴向下加速度9.8 */
        pRet->getPhysicsWorld()->setGravity(Vect(0,-9.8));
        pRet->autorelease();
    }
    /*  开启调试模式 调试模式可以将模拟的物理状态都绘制出来  */
    pRet->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    auto layer = Layer::create();
    pRet->addChild(layer);
    return pRet;
}


下面创建一个边框,用来防止屏幕上的刚体受到重力影响掉出屏幕,EdgeBox默认不受重力影像(静态刚体),在物理世界中若想要物体不受影像可通过setGravityEnable(false)方法实现。


auto node=Node::create();
/* 创建一个物体对象 EdgeBox是一个空心的矩形 相当于边框效果 */
auto bound =PhysicsBody::createEdgeBox(Size(visibleSize.width/2,visibleSize.height/2 ));
/* 设置刚体掩码 用于碰撞检测 */
//bound->setCategoryBitmask(1);
//bound->setCollisionBitmask(1);
bound->setContactTestBitmask(1);
/*   将物体与节点绑定  */
node->setPhysicsBody(bound);
node->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
this->addChild(node);

接下来在物理世界中再创建两个物体,并使用关节将它们连接起来

/* 创建圆形物体 a 参数半径 */
    auto body_a =PhysicsBody::createCircle(30.0f);
    body_a->setMass(10.0f);
    //body_a->setCategoryBitmask(1);
    //body_a->setCollisionBitmask(1);
    body_a->setContactTestBitmask(1);
    
    /* 创建矩形物体 b 参数宽高 */
    auto body_b =PhysicsBody::createBox(Size(100,100));
    body_b->setMass(20.0f);
    //body_b->setCategoryBitmask(1);
    //body_b->setCollisionBitmask(1);
    body_b->setContactTestBitmask(1);
    /* 可通过此函数为其设置速度 */
    //body_b->setVelocity(Vect(50.0f, 150.0f));

    /* 创建两个ImageView对象 用Sprite亦可  */
    auto img=ImageView::create();
    img->loadTexture("CloseNormal.png");
    img->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
    img->setPhysicsBody(body_a);
    this->addChild(img);
    
    auto img_2=ImageView::create();
    img_2->loadTexture("CloseNormal.png");
    img_2->setPosition(Point(visibleSize.width/3,visibleSize.height/2));
    img_2->setPhysicsBody(body_b);
    this->addChild(img_2);
    
    /* 创建关节,并将两个物体绑定到关节 */
    PhysicsJointLimit * limit=PhysicsJointLimit::construct(img->getPhysicsBody(), img_2->getPhysicsBody(), Point(0.0, 0.0), Point(0, 0));
    /* 将关节添加到物理世界  */
    getPhysicsWorld()->addJoint(limit);

这个时候运行项目实际上就已经能够看到效果了,最后来添加碰撞检测的事件。

    /* 创建监听器对象 */
    auto contactListener = EventListenerPhysicsContact::create();
    /* 设置onContactBegin回调函数为onContactBegin 开始碰撞调用 */
    contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
    /* 设置onContactSeperate回调函数为onContactSeperate 结束碰撞调用 */
    contactListener->onContactSeperate=CC_CALLBACK_1(HelloWorld::onContactSeperate, this);
    /* 设置监听 最后在释放函数中不要忘记取消注册 */
    _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);


此时运行项目可以看到效果图:

Cocos2d-x教程(30)-3.x版本物理引擎的使用


在上述过程中,我们所使用的碰撞体均为规则形状,然而在做精确碰撞检测时,所遇到的图形实际上大多为不规则图形,下面来演示不规则物体的创建。此处需要使用到一种工具,叫做VextexHelper

(下载地址:http://download.csdn.net/detail/u012945598/7725475)

下载之后解压,这个工具实际上就是一个Xcode下的工程,打开工程文件运行即可,效果图如下:

Cocos2d-x教程(30)-3.x版本物理引擎的使用


对于上图绿色边框部分为笔者所画出的边缘,之后该程序会根据各个点生成一个数组。我们真正需要用到的就是红色矩形里面的坐标,当然这个格式跟我们实际上的格式有些出入,事实上这个格式可以在该工具的代码中进行修改,使输出的类型变成Point。

将这些数据拷贝到我们的项目中就可以使用了:


    Point verts[] = {
        Point(-264.6f, -156.6f),
        Point(-265.4f, 158.2f),
        Point(-19.3f, 159.5f),
        Point(-20.8f, 178.1f),
        Point(18.3f, 177.8f),
        Point(18.3f, 162.4f),
        Point(239.6f, 159.7f),
        Point(258.3f, 160.7f),
        Point(259.3f, -142.3f),
        Point(258.1f, -156.4f),
        Point(20.2f, -160.1f),
        Point(19.2f, -174.2f),
        Point(-19.2f, -175.2f),
        Point(-21.4f, -160.8f),
        Point(-240.0f, -157.4f),
        Point(-265.5f, -157.8f)
    };
    
    auto sprite=Sprite::create("bg2.png");
    /* 创建一个多边形物体对象 参数为点数组与元素个数 */
    auto bound =PhysicsBody::createEdgePolygon(verts, 16);
    /*   将物体与精灵绑定  */
    sprite->setPhysicsBody(bound);
    sprite->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
    this->addChild(sprite);

运行效果如下:

Cocos2d-x教程(30)-3.x版本物理引擎的使用


最后,尽管Cocos对物理引擎进行了封装,但实际使用时需要注意的地方还有很多地方。