本文系原创,欢迎转载,转载时请附上本文链接,谢谢!
cocos2d-x中碰撞检测有两种方法,一个是用判断矩形时候相交,一个是用刚体,如果精灵少的话用矩形相交可能要简单呢,但是精灵多了用矩形相交来判断就显得有点麻烦,所以用刚体做碰撞检测就要显得自动方便些。
先做一些准备工作,因为有些朋友可能才接触游戏编程对刚体没有概念。
刚体这个概念来源于物理学,说实话我第一次看到刚体这个词的时候,我脑袋里只有一个反应就是这东西肯定很硬,因为刚嘛,而实际上确实很硬,硬得在受到任何力的作用下其内部的点与点之间的都不会改变。他只有位置,没有大小,没有形状.刚体在cocos2d-x中分为如下几类:
静态刚体:没有质量 没有速度 可以手动改变位置。
棱柱刚体:没有质量 可以有速度 可以自己更新位置。
动态刚体:有质量 有速度 有位置
在cocos2d-x中用刚体做碰撞检测的步骤是这样的;
第一步:创建世界,刚体只有在这个世界中才有效果,才能称为刚体
-
b2Vec2 gravity = b2Vec2(0.0f,0.0f);
-
m_pWord = new b2World(gravity);
-
m_pWord->SetAllowSleeping(false)
第二步:创建刚体:看注释很简单
-
void CCSkyLayer::CreateBody(CCSprite *pSprite)
-
{
-
b2BodyDef myBodyDef;
-
//刚体类型
-
myBodyDef.type = b2_dynamicBody;
-
myBodyDef.position.Set(pSprite->getPosition().x/PTM_RATIO, pSprite->getPosition().y/PTM_RATIO);
-
//和刚体绑定的精灵
-
myBodyDef.userData = pSprite;
-
EnterCriticalSection(&m_csWorld);
-
//用世界对象创建刚体
-
b2Body *pBody = m_pWord->CreateBody(&myBodyDef);
-
LeaveCriticalSection(&m_csWorld);
-
//定义刚体的形状描述
-
b2PolygonShape bodyShape;
-
//刚体的形状和精灵的形状一样.
-
bodyShape.SetAsBox(pSprite->getContentSize().width/PTM_RATIO/2,pSprite->getContentSize().height/PTM_RATIO/2);
-
//夹具:刚体用他来做碰撞检测.他定义了刚体的物理特性。
-
b2FixtureDef fix;
-
//指定刚体的形状
-
fix.shape = &bodyShape;
-
//刚体密度
-
fix.density = 10.0;
-
fix.isSensor = true;
-
fix.userData = pSprite;
-
pBody->CreateFixture(&fix);
-
}
第三步:这里假设你已经创建了和刚体绑定在一起的精灵pSprite.由于我们是通过和精灵绑定在一起的刚体来做碰撞检测的,所以在精灵位置发生变化的时候还要更新刚体的位置。精灵在哪里刚体就在哪里,并且两者大小一致。
创建一个定时器更新body的位置:
-
schedule(schedule_selector(CCSkyLayer::sch_selFuc_MoveBody), 0.01f);
更新代码如下,需要注意的是更新精灵位置的频率要和更新刚体位置的频率一致。否则检测会出现问题。比如:每隔0.1f更新一次精灵的位置,那么刚体也要每隔0.1f更新一次位置.
-
//让world对象每隔f时间就去检测所有的body位置.
-
m_pWord->Step(f, 1,1);
-
for(b2Body* body = m_pWord->GetBodyList(); body !=NULL; body = body->GetNext())
-
{
-
CCSprite *pSprite = static_cast<CCSprite *>(body->GetUserData());
-
if(pSprite)
-
{
-
b2Vec2 bVec2 = b2Vec2(pSprite->getPosition().x/PTM_RATIO , pSprite->getPosition().y/PTM_RATIO);
-
float32 b2Angle =-1* CC_RADIANS_TO_DEGREES(pSprite->getRotation());
-
body->SetTransform(bVec2, b2Angle);
-
}
-
}
在上面的更新代码中最核心的是m_pWord->Step(f,1,1);这个Step函数就是让世界对象知道在世界中哪些刚体发生了碰撞。这就是让物理引擎Box2d自动检测碰撞的原理所在。看下面的调试信息:
下面的代码中m_pWord->Step(f,1,1)最为关键.他是让世界对象知道在这个世界中哪些刚体发生了碰撞。下面的代码中m_pWord->Step(f,1,1)最为关键.他是让世界对象知道在这个世界中哪些刚体发生了碰撞。下面的代码中m_pWord->Step(f,1,1)最为关键.他是让世界对象知道在这个世界中哪些刚体发生了碰撞。
当发生了碰撞源代码b2Contact::Update中会调用一句代码:listener->BeginContact(this); listener的定义是:b2ContactListener* listener;
如何刚体发生了碰撞MyContactListener::BeginContact会被自动调用,而这个MyContactListener源代码里没有,是从b2ContactListener继承来的,懂了赛?这里就是用的多态。我们自己写了个类MyContactListener是从 b2ContactListener继承来的。BeginContact中。这里就引出了第四步.定义类MyContactListener.
第四步:定义MyContactListener
-
#pragma once
-
//#include "b2worldcallbacks.h"
-
#include "Box2D/Box2D.h"
-
#include "cocos2d.h"
-
#include <list>
-
using namespace std;
-
class MyContact
-
{
-
public :
-
b2Fixture *fixtureA,*fixtureB;
-
bool operator==(const MyContact& other) const
-
{
-
return (fixtureA == other.fixtureA) && (fixtureB == other.fixtureB);
-
}
-
};
-
-
class MyContactListener :public b2ContactListener
-
{
-
public:
-
CRITICAL_SECTION m_csContactList;
-
MyContactListener()
-
{
-
InitializeCriticalSection(&m_csContactList);
-
}
-
~MyContactListener()
-
{
-
DeleteCriticalSection(&m_csContactList);
-
}
-
list<MyContact> contactList;
-
virtual void BeginContact(b2Contact* contact)
-
{
-
if(contact)
-
{
-
MyContact mc;
-
mc.fixtureA=contact->GetFixtureA();
-
mc.fixtureB=contact->GetFixtureB();
-
EnterCriticalSection(&m_csContactList);
-
contactList.push_back(mc);
-
LeaveCriticalSection(&m_csContactList);
-
}
-
B2_NOT_USED(contact);
-
}
-
-
/// Called when two fixtures cease to touch.
-
virtual void EndContact(b2Contact* contact)
-
{
-
MyContact mc;
-
mc.fixtureA=contact->GetFixtureA();
-
mc.fixtureB=contact->GetFixtureB();
-
EnterCriticalSection(&m_csContactList);
-
list<MyContact>::iterator pos;
-
pos = find(contactList.begin(),contactList.end(),mc);
-
if(pos!=contactList.end())
-
{
-
contactList.erase(pos);
-
}
-
LeaveCriticalSection(&m_csContactList);
-
B2_NOT_USED(contact);
-
}
-
-
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
-
{
-
B2_NOT_USED(contact);
-
B2_NOT_USED(oldManifold);
-
}
-
-
-
virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
-
{
-
B2_NOT_USED(contact);
-
B2_NOT_USED(impulse);
-
}
-
}
当世界中的刚体不断发生碰撞。MyContactListener的虚函数BeginContact被调用,碰撞信息就存在了一个 list中。
要让碰撞的时候MyContactListener中的虚函数BeginContact被调用必须做第五步.实际上就是让基类的指针b2ContactListener *listener指向派生类MyContactListener的对象。
b2ContactListener* listener;
b2ContactListener* listenerb2ContactListener* listenerMyContactListenerlistener->BeginContact(this); 第五步:在创建世界对象 的代码下面添加:
-
_contactListener =new MyContactListener();
-
m_pWord->SetContactListener(_contactListener)
第六步: 到目前为止所有碰撞信息都存在list
<MyContact
> contactList
;中。只需要在一个定时器中随时监测这个list中的碰撞信息。就可以找到碰撞的刚体是那些,这些刚体对应的刚体是那些,然后对精灵做处理,比如相撞后的击退 或者 移除精灵等。
-
std::list<MyContact>::iterator bItor = _contactListener->contactList.begin();
-
std::list<MyContact>::iterator eItor = _contactListener->contactList.end();
-
b2Fixture *bFA = NULL;
-
b2Fixture *bFB = NULL;
-
for(;bItor != eItor ; ++ bItor)
-
{
-
bFA = bItor->fixtureA;
-
bFB = bItor->fixtureB;
-
CCSprite *pSpriteA = static_cast<CCSprite *>(bFA->GetBody()->GetUserData());
-
CCSprite *pSpriteB = static_cast<CCSprite *>(bFB->GetBody()->GetUserData());
-
//操作精灵
-
//...
-
//...
-
//...
-
}
好了 。完了。
阅读(4390) | 评论(0) | 转发(0) |