Cocos2d使用Box2d做碰撞处理

来源:互联网 发布:ie8 js serialize 编辑:程序博客网 时间:2024/06/16 01:31

在Cocos2d下已经留下了Box2d的接口,使用Box2d来做碰撞检测有效方便。

Box2d在与Cocos2d的世界进行对接的时候,要保证BOX2d的世界世界被同步的更新。如果设置了物理参数比如碰撞,重力,速度,加速度,那么将会产生非常逼真的效果。用来制作带有物理效果的也是十分方便的。

要使用Box2D的碰撞检测,大致要做下面几件事:

一、创建精灵的body,为body添加夹具,这个夹具就是在b2世界中用来物体的外形,根据此外性来进行碰撞检测。

二、周期性更新body的位置(按照每帧或每两帧等)。

三、打开碰撞监听,重写监听的处理函数,获取碰撞点信息保存到list。

四、周期性处理碰撞信息list,维护list。

接下来就分别介绍上述几步骤。


一、创建Body,添加夹具

先上代码:

b2Body* GameLayer::AddBoxBodyForSprite(cl* pcl,int type){if(this->m_world->IsLocked()){return NULL;}  b2BodyDef bd;                                          //创建body,设置位置  bd.type = b2_dynamicBody;  bd.position = b2Vec2((float)(pFish->getSpriteFish()->getPosition().x / PT_RATIO),(float)(pFish->getSpriteFish()->getPosition().y /PT_RATIO));  bd.userData = pcl; //userdata用来存放用户数据  b2Body* spriteBody = m_world->CreateBody(&bd);  CCString* FixName = CCString::createWithFormat("fish%02d_bodyft",type);   GB2ShapeCache *sc = GB2ShapeCache::sharedGB2ShapeCache(); //解析精灵外形多边形  sc->addFixturesToBody(spriteBody, FixName->getCString()); //按照多边形轮廓为精灵添加物理实体  return spriteBody;}
从上面的代码主要完成的就是添加body,然后创建body的夹具。在添加body时候注意在userdata上添加需要保存的内容,可以为变量或者指针,此例作为碰撞检测,应该保存碰撞实例的指针便于后面进行碰撞处理。

创建夹具使用PhysicsEditor工具,它可以手动选取精灵的轮廓编辑成plist文件,然后再程序中解析用于为该精灵创建合适的夹具。在选取轮廓是应该适中的选取点数,保证基本外形特征,同时尽量减少多边形的顶点,正阳Box2d在迭代计算式计算量小。


二、更新Box2d中物体的坐标

先贴上上代码。

if(this->m_world->IsLocked()){return ;}if (m_world)  m_world->Step(dt, 10, 10);    // 基于cocos2d的精灵位置来更新box2d的body位置    for(b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())    {        if(b&&b->GetUserData() != NULL)        {     for (b2Fixture* Fixture = b->GetFixtureList(); Fixture; Fixture = Fixture->GetNext())     {         b2Filter data = Fixture->GetFilterData(); group = data.groupIndex; break;     }     if(group==-2)    { Bullet* pBullet = (Bullet*)b->GetUserData(); if (pBullet&&pBullet->getSpriteBullet()) {      b2Vec2 pt = b2Vec2((float)(pBullet->getSpriteBullet()->getPosition().x / PT_RATIO), (float)(pBullet->getSpriteBullet()->getPosition().y / PT_RATIO));      float angle = CC_DEGREES_TO_RADIANS(pBullet->getSpriteBullet()->getRotation());      b->SetTransform(pt, -angle);//与cocos中转向相反 }    }        }    }
首先,在更新坐标box2d世界坐标以前,先将PTM_RATIO这个数一般定义为: 32.0,在box 世界中 是以 米 为单位的,这里是将坐标兑换为box世界中的米。这就相当一个比例尺,在box2d的世界里面,最佳的尺寸就是10m以内,进行一个缩放转换。

当然首先要创建一b2world:

void GameLayer::InitB2World(){b2Vec2 gravity(0.0f, -0.0f);bool doSleep = false;m_world = new b2World(gravity);m_world->SetAllowSleeping(doSleep);m_world->SetContinuousPhysics(true);contactListener = new MyContactListener();}
在创建被world的时候可以设置是否存在重力,是否可以休眠,设置监听。

在更新时通过m_word的实例来遍历其中的每一个body,通过body的userdata查询精灵并利用精灵来更新物体在世界中的位置。注意在更新以前一定的保证上一次迭代完成,通过判断是否世界上锁来得到当前状态。

三、碰撞监听

打开碰撞监听最好重写一个继承自b2ContactListener类的类,然后覆盖碰撞响应函数 ,在响应函数得到碰撞点信息。代码MyContact.h:

class MyContact{public:  b2Fixture* fixtureA;  b2Fixture* fixtureB;  b2Vec2 contactPoint;bool operator==(const MyContact& other) const    {        return (fixtureA == other.fixtureA) && (fixtureB == other.fixtureB);    }};class MyContactListener : public b2ContactListener {public:MyContactListener();~MyContactListener();std::list<MyContact> contact_list;//std::vector<MyContact>_contacts; virtual void BeginContact(b2Contact *contact);//virtual void EndContact(b2Contact *contact);virtual void PreSolve(b2Contact *contact, const b2Manifold *oldManifold);virtual void PostSolve(b2Contact *contact, const b2ContactImpulse *impulse);};
在MyContact类中也定义了一个contact_list用来保存碰撞点的信息,定义MyContact结构体来格式化存储碰撞点。下面是MyContact.cpp代码:

#include "MyContact.h"MyContactListener::MyContactListener(){}MyContactListener::~MyContactListener() {}void MyContactListener::BeginContact(b2Contact *contact) { if (contact)    {      MyContact mc;      mc.fixtureA = contact->GetFixtureA();      mc.fixtureB = contact->GetFixtureB();   for( std::list<MyContact>::iterator it = contact_list.begin();//重要,本例一次只能采样一个碰撞点                              it != this->contact_list.end();                              ++it) { MyContact& contact = *it; if((contact.fixtureA->GetUserData() ==  mc.fixtureA->GetUserData())&& (contact.fixtureB->GetUserData() == contact.fixtureB->GetUserData())) { return ; } else if((contact.fixtureA->GetUserData() ==  mc.fixtureB->GetUserData())&& (contact.fixtureB->GetUserData() == contact.fixtureA->GetUserData())) { return ; } }     //定义一个b2WorldManifold对象,用来获取并存储碰撞点的全局坐标     b2WorldManifold* manifold = new b2WorldManifold();     //通过GetWorldManifolde方法,计算出碰撞点的全局坐标,并存储到manifold变量中 contact->GetWorldManifold(manifold);     //获取碰撞点     mc.contactPoint = manifold->points[0];     contact_list.push_back(mc);    }  //  B2_NOT_USED(contact);   } //当刚体发生碰撞开始的时候调用void MyContactListener::EndContact(b2Contact *contact) {}//当刚体结束碰撞时调用void MyContactListener::PreSolve(b2Contact *contact, const b2Manifold *oldManifold){B2_NOT_USED(contact);    B2_NOT_USED(oldManifold);} //碰撞开始前调用void MyContactListener::PostSolve(b2Contact *contact, const b2ContactImpulse *impulse) {B2_NOT_USED(contact);    B2_NOT_USED(impulse);}
一共有四个碰撞处理函数,分别是碰撞开始时候,碰撞过程中,碰撞预处理。需要重写那个函数根须实际需要决定。但需要注意同一时间可能两个物体有多个碰撞点存在,注意排除重复点。

四、碰撞处理函数

如果碰撞点很多处理起来就很吃力,需要想办法分支节约效率,同时要保证处理的实时性,不能对一个碰撞

  for( std::list<MyContact>::iterator it = contactListener->contact_list.begin();                               it != contactListener->contact_list.end();                              ++it)  {        .....  } contactListener->contact_list.clear();//清空待待处理的contactlist,防止重复出错
便利碰撞list记录,根据userdata处理。同时在最后记得清空整个list。

以上几个步骤基本上就已经开阔了碰撞处理的基本方法,调试时可以打开调试绘图查看碰撞区域,以及触发时间。

打开碰撞调试区域显示:

debugDraw = new GLESDebugDraw(PT_RATIO);   //这里新建一个 debug渲染模块  m_world->SetDebugDraw(debugDraw);    //设置  uint32 flags = 0;     flags += b2Draw::e_shapeBit ;  //b2Draw::e_centerOfMassBit;   //获取需要显示debugdraw的块  //b2Draw::e_aabbBit;  //AABB块  //b2Draw::e_centerOfMassBit; 物体质心  //b2Draw::e_jointBit;  //关节  //b2Draw::e_shapeBit;   形状  debugDraw->SetFlags(flags);   //需要显示那些东西  draw();   //画出来  
最后通过调用draw函数进行会图,draw中那些如下代码:

CCNode::draw();ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position);kmGLPushMatrix(); m_world->DrawDebugData();   //这个一定要写  kmGLPopMatrix();
通过上面的实质将会吧碰撞多边形显示出来,特变注意,显示调试区域的时候,一定要将场景图的背景去掉,直接以黑双色为背景,否则调试区域可能显示不出来。






0 0
原创粉丝点击