我一直在阅读有关如何编写联系人监听器的各种教程,但我无法理解它。
这是我到目前为止所拥有的: 在我代表物理对象的每个类中,我做了:
_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)我仍然不会像地形那样正常碰撞猫。
接下来我该怎么办?我对于该怎么做很无能为力?如何让猫继续与地形正常碰撞,而不是鼠标?我该什么时候取出鼠标?
答案 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。 从系统中删除死实体。
这有用吗?