如何在cocos2d中通过box2d检测碰撞

时间:2013-10-09 12:06:06

标签: cocos2d-iphone box2d

我有两个精灵:一个角色精灵,另一个是障碍精灵。障碍精灵是另一个叫做bgSprite的精灵的孩子,它不断移动。我怎样才能检测到它们之间的碰撞。 我正在使用CGRECTINTESECTRECT,但它看起来不太现实。我听说过box2d,但我还没用过它。这是一些代码:

[[CCSpriteFrameCache sharedSpriteFrameCache]     
addSpriteFramesWithFile:@"BoyRunAnimation.plist"];

CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode     
batchNodeWithFile:@"BoyRunAnimation.png"];
[self addChild:spriteSheet];        

self._character = [CCSprite spriteWithSpriteFrameName:@"Boy_Run_0003.png"];
self._character.position = ccp(80, 150);
[spriteSheet addChild:self._character];
[self boyRunningAnimation]; 

//障碍

for (int i=0; i<5; i++)
{
int xPos=500+500*i;
if (xPos<2*_roadImage1.contentSize.width)
{
    CCSprite *obstacle=[CCSprite node];
    obstacle.textureRect=CGRectMake(0, 0, 50, _roadImage1.contentSize.height);
    obstacle.color=ccc3(255, 255,255);

    if (xPos <= _roadImage1.contentSize.width)
    {
        obstacle.position=ccp(xPos, _roadImage1.contentSize.height/2);

        [_roadImage1 addChild:obstacle z:0 tag:1];
    }
    else
    {
        obstacle.position=ccp(xPos-_roadImage1.contentSize.width, 60);

        [_roadImage2 addChild:obstacle z:0 tag:2];
    }
    [obstacleArray addObject:obstacle];
 }    
 }
更新方法中的

  CCRect obstacleBox = [obstacle boundingBox];
  CCPoint obstaclePosition = obstacleBox.origin;
  obstaclePosition = [[obstacle parent] convertToWorldSpace:obstaclePosition];
  obstaclePosition = [[self._character parent] convertToNodeSpace:obstaclePosition];
  obstacleBox.origin = obstaclePosition;
  if (CGRectIntersectsRect(self._character.boundingBox, obstacleBox))
  {
    isTouchActive=NO;
    NSLog(@"collision");
   }

请帮帮我。

2 个答案:

答案 0 :(得分:1)

如果您使用的是box2d,则在处理世界更新时,会在Box2d引擎中处理碰撞检测。这可以检测身体上的固定装置之间的碰撞,而不是身体本身。因此,如果你的身体是由一组固定装置组成的,你可以将身体的一小部分检测到,以使爆炸(或其他)的位置看起来很好。

简单的答案是从b2ContactListener派生一个类并告诉b2World你想要使用它。手册中的示例如下所示:

class MyContactListener : public b2ContactListener 
{
public:
void BeginContact(b2Contact* contact) { /* handle begin event */ }
void EndContact(b2Contact* contact) { /* handle end event */ }
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) { /* handle pre-solve event */ }
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{ /* handle post-solve event */ } 
};

您在派生类的实例上使用b2World :: SetContactListener(...)注册它。

但是,它并不那么简单。我的经验是,对于单个“碰撞”,您可以在世界更新期间多次拨打您的班级。

以下是更完整的解决方案。在这里,世界上的每个“东西”都有一个b2Body,用户标签指向根类(称为Entity)。它累积了一个冲突列表并检查每个新事件是否引用了已经看到的一对,过滤掉了重复项。在更新世界更新后,会调用NotifyCollisions向发生冲突的实体发送消息,并且需要对其进行一些操作。

这是更大代码库的一部分;如果您需要澄清,请随时提出任何问题,因为代码不在此处。

class EntityContactListener : public ContactListener
{
private:
   GameWorld* _gameWorld;
   EntityContactListener() {}

   typedef struct 
   {
      Entity* entA;
      Entity* entB;
   } CONTACT_PAIR_T;

   vector<CONTACT_PAIR_T> _contactPairs;

public:
   virtual ~EntityContactListener() {}

   EntityContactListener(GameWorld* gameWorld) :
      _gameWorld(gameWorld)
   {
      _contactPairs.reserve(128);
   }

   void NotifyCollisions()
   {
      Message* msg;
      MessageManager& mm = GameManager::Instance().GetMessageMgr();

      for(uint32 idx = 0; idx < _contactPairs.size(); idx++)
      {
         Entity* entA = _contactPairs[idx].entA;
         Entity* entB = _contactPairs[idx].entB;

         //DebugLogCPP("Contact Notification %s<->%s",entA->ToString().c_str(),entB->ToString().c_str());

         msg = mm.CreateMessage();
         msg->Init(entA->GetID(), entB->GetID(), Message::MESSAGE_COLLISION);
         mm.EnqueueMessge(msg, 0);

         msg = mm.CreateMessage();
         msg->Init(entB->GetID(), entA->GetID(), Message::MESSAGE_COLLISION);
         mm.EnqueueMessge(msg, 0);         
      }
      _contactPairs.clear();
   }

   void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
   {

   }

   // BEWARE:  You may get multiple calls for the same event.
   void BeginContact(b2Contact* contact)
   {
      Entity* entA = (Entity*)contact->GetFixtureA()->GetBody()->GetUserData();
      Entity* entB = (Entity*)contact->GetFixtureB()->GetBody()->GetUserData();
      //DebugLogCPP("Begin Contact %s->%s",entA->ToString().c_str(),entB->ToString().c_str());
      if(entA->GetGroupID() == entB->GetGroupID())
      {  // Can't collide if they are in the same group.
         return;
      }

      assert(entA != NULL);
      assert(entB != NULL);

      for(uint32 idx = 0; idx < _contactPairs.size(); idx++)
      {
         if(_contactPairs[idx].entA == entA && _contactPairs[idx].entB == entB)
            return;
         // Not sure if this is needed...
         if(_contactPairs[idx].entA == entB && _contactPairs[idx].entA == entB)
            return;
      }
      CONTACT_PAIR_T pair;
      pair.entA = entA;
      pair.entB = entB;
      _contactPairs.push_back(pair);
   }

   // BEWARE:  You may get multiple calls for the same event.
   void EndContact(b2Contact* contact)
   {
      /*
      Entity* entA = (Entity*)contact->GetFixtureA()->GetBody()->GetUserData();
      Entity* entB = (Entity*)contact->GetFixtureB()->GetBody()->GetUserData();
      DebugLogCPP("End Contact %s->%s",entA->ToString().c_str(),entB->ToString().c_str());
       */
   }
};

这有用吗?

答案 1 :(得分:0)

在COCOS2D-X

首先,你要为你的精灵制作身体。并根据精灵移动身体。

您必须使用b2contactlistener扩展您的课程。您必须在代码中实现的方法很少。 您可以阅读本文以获得清晰度 Box2D for Collision Detection with Cocos2D-X | Cocos2d-x