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