如何使用cocos2d编写box2d联系人监听器?

时间:2014-01-10 11:34:51

标签: objective-c cocos2d-iphone box2d

我一直在阅读有关如何编写联系人监听器的各种教程,但我无法理解它。

这是我到目前为止所拥有的: 在我代表物理对象的每个类中,我做了:

_body->SetUserData(self);

我编写了一个包含以下两种方法的联系人监听器类:

void ContactListener::BeginContact(b2Contact* contact)
{
    // Box2d objects that collided
    b2Fixture* fixtureA = contact->GetFixtureA();
    b2Fixture* fixtureB = contact->GetFixtureB();
    // Sprites that collided
    MyNode* actorA = (MyNode*) fixtureA->GetBody()->GetUserData();
    MyNode* actorB = (MyNode*)  fixtureB->GetBody()->GetUserData();
}

void ContactListener::EndContact(b2Contact* contact)
{
    // Box2d objects that collided
    b2Fixture* fixtureA = contact->GetFixtureA();
    b2Fixture* fixtureB = contact->GetFixtureB();
    // Sprites that collided
    MyNode* actorA = (MyNode*) fixtureA->GetBody()->GetUserData();
    MyNode* actorB = (MyNode*)  fixtureB->GetBody()->GetUserData();
}

我不知道接下来该做什么。我现在有两个精灵碰撞,但我想做以下事情: 1)当它们碰撞时,我想根据物体的类型从世界中移除一个精灵。 (例如,如果一个猫对象而另一个是鼠标对象,我想删除鼠标对象。

2)我想让猫对象知道它吃了一只老鼠

3)我希望猫继续移动,好像它没有接触鼠标一样。

4)我仍然不会像地形那样正常碰撞猫。

接下来我该怎么办?我对于该怎么做很无能为力?如何让猫继续与地形正常碰撞,而不是鼠标?我该什么时候取出鼠标?

1 个答案:

答案 0 :(得分:0)

拥有一个“实体”类来保存对Box2D主体的引用并对其进行操作绝对是一个很好的方法。如果你有一个Spaceship类而不是Meteor类,它们每个都可以提供自己的控制体(AI)的派生方法,但是它们每个都有共同的逻辑和代码来支持对“有身体的东西”的操作(例如,常见的“实体”基类)。我认为你走在正确的轨道上。

当联系人开始发生时,它变得有点模糊。这是您开始进入整个系统架构的地方,而不仅仅是物理世界的结构或单个Coco2d场景。

以下是我过去的做法:

首先,我设置了联系人监听器,如下所示:

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)
   {
      b2Fixture* fixtureA = contact->GetFixtureA();
      b2Body* bodyA = fixtureA->GetBody();
      Entity* entityA = bodyA->GetUserData();
      b2Fixture* fixtureB = contact->GetFixtureB();
      b2Body* bodyB = fixtureB->GetBody();
      Entity* entityB = bodyB->GetUserData();

      if(test if entityA and entityB should not have collision response)
      {
         contact->SetEnabled(false);
      }
      // Do this if you want there to be collision notification, even if
      // there is no response.
      AddContactPair(entA,entB);
   }

   void AddContactPair(Entity* entA, Entity* entB)
   {
      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 BeginContact(b2Contact* contact)
   {
      Entity* entA = (Entity*)contact->GetFixtureA()->GetBody()->GetUserData();
      Entity* entB = (Entity*)contact->GetFixtureB()->GetBody()->GetUserData();

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

      // Not sure this is still needed if you add it in the pre-solve.
      // May not be necessary...
      AddContactPair(entA, entB);
   }

   // BEWARE:  You may get multiple calls for the same event.
   void EndContact(b2Contact* contact)
   {
   }
};

由于引擎的工作方式,您可以为同一个机构获得多个联系人命中。此侦听器会过滤它们,因此如果两个实体发生冲突,您只会收到一条消息。

监听器仅存储发生的冲突。它可以被修改以进一步将它们分成“开始”和“结束”以用于其他目的。在这里,联系意味着“你被某些东西击中”。我不需要知道它是否停止接触。

对NotifyCollisions的调用是“秘密酱”。它向所联系的实体(通过消息系统)发送一条消息,表明他们遇到了某些内容而另一个实体就是他们所击中的内容。子弹击中了船。子弹破坏了自我。船舶损坏自我基于子弹属性(GetDamageInflicted()方法)。这反过来表示图形系统从显示器上移除子弹。如果船被摧毁,它也会被摧毁。

从整体执行的角度来看: 在开始之前,请指定联系人侦听器。

游戏的每个周期: 在您的所有实体上调用“更新”。这会更新他们的物理力量等。 更新Box2d世界。 在监听器上调用NotifyCollisions。 从系统中删除死实体。

这有用吗?