Chinaunix首页 | 论坛 | 博客
  • 博客访问: 26900
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 72
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-22 14:28
文章分类
文章存档

2013年(5)

我的朋友

分类: Java

2013-05-28 13:39:15

本文系原创,欢迎转载,转载时请附上本文链接,谢谢!

cocos2d-x中碰撞检测有两种方法,一个是用判断矩形时候相交,一个是用刚体,如果精灵少的话用矩形相交可能要简单呢,但是精灵多了用矩形相交来判断就显得有点麻烦,所以用刚体做碰撞检测就要显得自动方便些。

先做一些准备工作,因为有些朋友可能才接触游戏编程对刚体没有概念。
刚体这个概念来源于物理学,说实话我第一次看到刚体这个词的时候,我脑袋里只有一个反应就是这东西肯定很硬,因为刚嘛,而实际上确实很硬,硬得在受到任何力的作用下其内部的点与点之间的都不会改变。他只有位置,没有大小,没有形状.刚体在cocos2d-x中分为如下几类:
静态刚体:没有质量 没有速度 可以手动改变位置。
棱柱刚体:没有质量  可以有速度  可以自己更新位置。
动态刚体:有质量 有速度  有位置

在cocos2d-x中用刚体做碰撞检测的步骤是这样的;
第一步:创建世界,刚体只有在这个世界中才有效果,才能称为刚体

点击(此处)折叠或打开

  1. b2Vec2 gravity = b2Vec2(0.0f,0.0f);
  2.  m_pWord = new b2World(gravity);
  3.  m_pWord->SetAllowSleeping(false)

第二步:创建刚体:看注释很简单


点击(此处)折叠或打开

  1. void CCSkyLayer::CreateBody(CCSprite *pSprite)
  2. {
  3.     b2BodyDef myBodyDef;
  4.     //刚体类型
  5.     myBodyDef.type = b2_dynamicBody;
  6.     myBodyDef.position.Set(pSprite->getPosition().x/PTM_RATIO, pSprite->getPosition().y/PTM_RATIO);
  7.     //和刚体绑定的精灵
  8.     myBodyDef.userData = pSprite;
  9.     EnterCriticalSection(&m_csWorld);
  10.     //用世界对象创建刚体
  11.     b2Body *pBody = m_pWord->CreateBody(&myBodyDef);
  12.     LeaveCriticalSection(&m_csWorld);
  13.     //定义刚体的形状描述
  14.     b2PolygonShape bodyShape;
  15.     //刚体的形状和精灵的形状一样.
  16.     bodyShape.SetAsBox(pSprite->getContentSize().width/PTM_RATIO/2,pSprite->getContentSize().height/PTM_RATIO/2);
  17.     //夹具:刚体用他来做碰撞检测.他定义了刚体的物理特性。
  18.     b2FixtureDef fix;
  19.     //指定刚体的形状
  20.     fix.shape = &bodyShape;
  21.     //刚体密度
  22.     fix.density = 10.0;
  23.     fix.isSensor = true;
  24.     fix.userData = pSprite;
  25.     pBody->CreateFixture(&fix);
  26. }
第三步:这里假设你已经创建了和刚体绑定在一起的精灵pSprite.由于我们是通过和精灵绑定在一起的刚体来做碰撞检测的,所以在精灵位置发生变化的时候还要更新刚体的位置。精灵在哪里刚体就在哪里,并且两者大小一致。
创建一个定时器更新body的位置:

点击(此处)折叠或打开

  1. schedule(schedule_selector(CCSkyLayer::sch_selFuc_MoveBody), 0.01f);
更新代码如下,需要注意的是更新精灵位置的频率要和更新刚体位置的频率一致。否则检测会出现问题。比如:每隔0.1f更新一次精灵的位置,那么刚体也要每隔0.1f更新一次位置.

点击(此处)折叠或打开

  1. //让world对象每隔f时间就去检测所有的body位置.
  2.     m_pWord->Step(f, 1,1);
  3.     for(b2Body* body = m_pWord->GetBodyList(); body !=NULL; body = body->GetNext())
  4.     {
  5.         CCSprite *pSprite = static_cast<CCSprite *>(body->GetUserData());
  6.         if(pSprite)
  7.         {
  8.             b2Vec2 bVec2 = b2Vec2(pSprite->getPosition().x/PTM_RATIO , pSprite->getPosition().y/PTM_RATIO);
  9.             float32 b2Angle =-1* CC_RADIANS_TO_DEGREES(pSprite->getRotation());
  10.             body->SetTransform(bVec2, b2Angle);
  11.         }
  12.     }
在上面的更新代码中最核心的是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

点击(此处)折叠或打开

  1. #pragma once
  2. //#include "b2worldcallbacks.h"
  3. #include "Box2D/Box2D.h"
  4. #include "cocos2d.h"
  5. #include <list>
  6. using namespace std;
  7. class MyContact
  8. {
  9. public :
  10.    b2Fixture *fixtureA,*fixtureB;
  11.    bool operator==(const MyContact& other) const
  12.     {
  13.          return (fixtureA == other.fixtureA) && (fixtureB == other.fixtureB);
  14.     }
  15. };

  16. class MyContactListener :public b2ContactListener
  17. {
  18. public:
  19.     CRITICAL_SECTION m_csContactList;
  20.     MyContactListener()
  21.     {
  22.         InitializeCriticalSection(&m_csContactList);
  23.     }
  24.     ~MyContactListener()
  25.     {
  26.         DeleteCriticalSection(&m_csContactList);
  27.     }
  28.     list<MyContact> contactList;
  29.     virtual void BeginContact(b2Contact* contact)
  30.     {
  31.              if(contact)
  32.             {
  33.                      MyContact mc;
  34.                      mc.fixtureA=contact->GetFixtureA();
  35.                      mc.fixtureB=contact->GetFixtureB();
  36.                      EnterCriticalSection(&m_csContactList);
  37.                     contactList.push_back(mc);
  38.                     LeaveCriticalSection(&m_csContactList);
  39.              }
  40.              B2_NOT_USED(contact);
  41.    }

  42.   /// Called when two fixtures cease to touch.
  43.     virtual void EndContact(b2Contact* contact)
  44.     {
  45.         MyContact mc;
  46.         mc.fixtureA=contact->GetFixtureA();
  47.         mc.fixtureB=contact->GetFixtureB();
  48.         EnterCriticalSection(&m_csContactList);
  49.         list<MyContact>::iterator pos;
  50.         pos = find(contactList.begin(),contactList.end(),mc);
  51.         if(pos!=contactList.end())
  52.         {
  53.             contactList.erase(pos);
  54.         }
  55.         LeaveCriticalSection(&m_csContactList);
  56.              B2_NOT_USED(contact);
  57.         }
  58.     
  59.         virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
  60.         {
  61.              B2_NOT_USED(contact);
  62.              B2_NOT_USED(oldManifold);
  63.          }
  64.     
  65.     
  66.     virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
  67.     {
  68.      B2_NOT_USED(contact);
  69.      B2_NOT_USED(impulse);
  70.      }
  71. }
当世界中的刚体不断发生碰撞。MyContactListener的虚函数BeginContact被调用,碰撞信息就存在了一个 list中。
要让碰撞的时候MyContactListener中的虚函数BeginContact被调用必须做第五步.实际上就是让基类的指针b2ContactListener *listener指向派生类MyContactListener的对象。
b2ContactListener* listener;
b2ContactListener* listenerb2ContactListener* listenerMyContactListenerlistener->BeginContact(this);  第五步:在创建世界对象 的代码下面添加:

点击(此处)折叠或打开

  1. _contactListener =new MyContactListener();
  2.     m_pWord->SetContactListener(_contactListener)
第六步: 到目前为止所有碰撞信息都存在list<MyContact> contactList;中。只需要在一个定时器中随时监测这个list中的碰撞信息。就可以找到碰撞的刚体是那些,这些刚体对应的刚体是那些,然后对精灵做处理,比如相撞后的击退  或者  移除精灵等。


点击(此处)折叠或打开

  1.     std::list<MyContact>::iterator bItor = _contactListener->contactList.begin();
  2.     std::list<MyContact>::iterator eItor = _contactListener->contactList.end();
  3.     b2Fixture *bFA = NULL;
  4.     b2Fixture *bFB = NULL;
  5.     for(;bItor != eItor ; ++ bItor)
  6.     {
  7.         bFA = bItor->fixtureA;
  8.         bFB = bItor->fixtureB;
  9.         CCSprite *pSpriteA = static_cast<CCSprite *>(bFA->GetBody()->GetUserData());
  10.         CCSprite *pSpriteB = static_cast<CCSprite *>(bFB->GetBody()->GetUserData());
  11.         //操作精灵
  12.         //...
  13.         //...
  14.         //...
  15.     }

好了 。完了。
阅读(4390) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~