【问题标题】:How to write a box2d contact listener using cocos2d?如何使用 cocos2d 编写 box2d 联系人监听器?
【发布时间】:2014-01-10 22:56:43
【问题描述】:

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

这是我目前所拥有的: 在我代表物理对象的每个类中:

_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) 我仍然不希望猫与地形之类的东西正常碰撞。

接下来我该怎么做?我很不知道该怎么办?如何让猫继续与地形正常碰撞,但不与鼠标碰撞?什么时候移除鼠标?

【问题讨论】:

    标签: objective-c cocos2d-iphone box2d


    【解决方案1】:

    拥有一个“实体”类来保存对 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。 从系统中移除死亡实体。

    这有帮助吗?

    【讨论】:

    • 我还是迷路了。我有 3 个实体 - 猫、老鼠、地形。猫和老鼠都应该与地形发生碰撞,但不能相互碰撞。当猫与鼠标碰撞时(反之亦然),我不希望它们成为碰撞响应,我只想移除鼠标,让猫继续正常运行。根据您的代码,我知道如何获取进行接触的实体(我已经有类似的东西),但我不知道如何防止物理碰撞继续/或允许它继续基于它是猫/鼠标接触还是猫/地形或鼠标/地形接触。
    • 我修改了 PreSolve 事件来处理这个问题。您在那里检测到碰撞,取消碰撞的继续处理(contact->SetEnabled(false)),但将其添加到“已联系”列表中,以便稍后进行处理。在手册中查找 PreSolve 事件,它使用了一个非常相似的示例。
    • @John 这回答了你的问题吗?
    • 不完全是——我不明白你的解决方案——也许你更新了它,因为它现在看起来更简单了。我使用了一个稍微简单的解决方案,使用了碰撞面罩、传感器和一个接触监听器。但是在找出我自己的解决方案之后,我更了解你的解决方案。我仍然不确定哪种设计更好。但是现在我的问题已经解决了。我认为,如果您能更好地解释 presolve 中发生的事情,这个答案会帮助更多的人。
    • 我使用了碰撞遮罩,所以猫/老鼠只与地形发生碰撞,而不会相互碰撞。然后我在 cat 和 mouse 上使用了另一个夹具(传感器),它使用了接触侦听器。我将猫/鼠标的联系人存储在一个向量中,我在模拟物理后每次更新都会读取该向量。我没有写预求解或后求解方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-05
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多