无法在 box2d 碰撞回调中获取类数据

Posted

技术标签:

【中文标题】无法在 box2d 碰撞回调中获取类数据【英文标题】:Cannot get class data in box2d collision callback 【发布时间】:2018-11-18 16:11:02 【问题描述】:

我正在用 C++ 构建一个简单的 2D 游戏,并使用 Box2D 进行碰撞检测。

我有一个Entity 类,EnemyBullet 类派生自该类,EnemySquare 类派生自Enemy 类。

我正在尝试检测 EnemySquare 类和 Bullet 类之间的冲突(稍后将有更多的冲突组合需要处理)。为此,我创建了一个派生自 Box2D 类 b2ContactListenerCollisionManager 类,该类处理碰撞回调。

每个Entity 实例都有一个私有变量m_collisionObjectType,它是对象类型的enum class(如下所示)。

BeginContact() 回调中,我正在尝试将 box2d 夹具的用户数据转换为正确的类类型,以便我可以应用损坏、标记要移除的子弹等。

(为简单起见,删除了不相关的代码)

对象类型枚举:

enum class COLLISION_OBJECT_TYPE BULLET, ENEMY, PLAYER;

实体类

.h

class Entity

public:
    Entity();
    ~Entity();

    COLLISION_OBJECT_TYPE getCollisionObjectType()  return m_collisionObjectType; 

protected:

    b2Body* m_body = nullptr;
    b2Fixture* m_fixtures[3];
    COLLISION_OBJECT_TYPE m_collisionObjectType;

;

敌人等级

.h

class Enemy : public Entity

public:
    Enemy();
    ~Enemy();
    virtual void init(glm::vec2 position, float health, float speed, Player* player, b2World* physicsWorld) = 0;
    virtual void update(float deltaTime) = 0;

protected:

    float m_health;
    float m_speed;
    Player* m_playerTarget;

;

敌方类

.h

class EnemySquare : public Enemy

public:
    EnemySquare();
    ~EnemySquare();

    void init(glm::vec2 position, float health, float speed, Player* player, b2World* physicsWorld) override;
    void update(float deltaTime) override;

;

.cpp

void EnemySquare::init(glm::vec2 position, float health, float speed, Player * player, b2World* physicsWorld) 

    // init physics body
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(m_position.x, m_position.y);
    bodyDef.fixedRotation = false;
    bodyDef.angle = 0;
    bodyDef.userData = this;
    m_body = physicsWorld->CreateBody(&bodyDef);

    // init physics fixtures
    b2PolygonShape squareShape;
    squareShape.SetAsBox(m_width * 0.5f, m_height * 0.5f);
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &squareShape;
    m_fixtures[0] = m_body->CreateFixture(&fixtureDef);


子弹类

.h

class Bullet : public Entity

public:
    Bullet(
        b2World* world,
        glm::vec2 startPosition,
        glm::vec2 direction,
        Tempest::glTexture texture,
        float width,
        float height,
        float damage,
        float speed,
        float range
    );
    ~Bullet();

    // methods are unrelated

private:
    // private variables are unrelated

;

.cpp

Bullet::Bullet(
    b2World* world,
    glm::vec2 startPosition,
    glm::vec2 direction,
    Tempest::glTexture texture,
    float width,
    float height,
    float damage,
    float speed,
    float range
) 

    // Make the body
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(m_position.x, m_position.y);
    bodyDef.fixedRotation = true;
    bodyDef.angle = 0;
    bodyDef.userData = this;
    m_body = world->CreateBody(&bodyDef);

    // Create the box
    b2PolygonShape boxShape;
    boxShape.SetAsBox(m_height * 0.4f, m_width * 0.5f);

    b2FixtureDef boxDef;
    boxDef.shape = &boxShape;
    m_fixtures[0] = m_body->CreateFixture(&boxDef);

    m_collided = false;

    m_collisionObjectType = COLLISION_OBJECT_TYPE::BULLET;


在我的 CollisionManager 类中,我试图检索碰撞夹具的 userData(这是一个 void*),然后将其转换为 Entity* 以调用 getCollisionObjectType() 方法。当我知道我正在处理什么类型的实体时,我想将其转换为正确的对象类型并执行诸如施加伤害、标记要移除的子弹等操作。代码如下:

void CollisionManager::BeginContact(b2Contact * contact) 

    void* fixtureABodyData = contact->GetFixtureA()->GetBody()->GetUserData();
    void* fixtureBBodyData = contact->GetFixtureB()->GetBody()->GetUserData();
    if (fixtureABodyData && fixtureBBodyData) 
        Entity* fixtureAData = static_cast<Entity*>(fixtureABodyData);
        Entity* fixtureBData = static_cast<Entity*>(fixtureBBodyData);
        if (fixtureAData->getCollisionObjectType() == COLLISION_OBJECT_TYPE::BULLET) 
            std::cout << "A BULLET" << std::endl;
        
        if (fixtureBData->getCollisionObjectType() == COLLISION_OBJECT_TYPE::BULLET) 
            std::cout << "B BULLET" << std::endl;
        
        if (fixtureAData->getCollisionObjectType() == COLLISION_OBJECT_TYPE::ENEMY) 
            std::cout << "A ENEMY" << std::endl;
        
        if (fixtureBData->getCollisionObjectType() == COLLISION_OBJECT_TYPE::ENEMY) 
            std::cout << "B ENEMY" << std::endl;
        
        std::cout << "----------------------" << std::endl;

由于某种原因,投射适用于 Bullet 类,但不适用于 Enemy 类。我认为它返回一个nullptr。所以我知道其中一个碰撞的物体是一颗子弹,但我不知道第二个物体是什么。

我感觉我在调用 static_cast 时做错了,或者可能是因为 EnemySquare 类从 Entity 类中删除了两次?或者我可能在 Box2D 代码中做错了什么。任何建议将不胜感激!

【问题讨论】:

您好!关于static_casts 看看type casting。我觉得应该是reinterpret_cast 嘿,非常感谢您的建议,不幸的是仍然没有运气。不过我会继续研究reinterpret_cast,感觉这是一个很好的解决方案,我只是可能需要弄清楚如何正确使用它。 【参考方案1】:

Enemy 派生类设置m_collisionObjectType 成员变量。最好是COLLISION_OBJECT_TYPE::ENEMY

正如代码现在出现的那样,它只是在Bullet 派生类的代码中设置m_collisionObjectType。因此,Enemy 派生类实例在构造时使用未初始化的 m_collisionObjectType 成员变量。 IE。 getCollisionObjectType() 方法的值可以是 m_collisionObjectType 的内存位置在构造函数使用之前的任何值。

希望这会有所帮助!

【讨论】:

以上是关于无法在 box2d 碰撞回调中获取类数据的主要内容,如果未能解决你的问题,请参考以下文章

边缘 - Cocos2d/Box2D

SpriteKit物理引擎碰撞中5个重要信息

Box2d Cocos2d 中的联系监听器

Cocos2d-x 3.0中 物理碰撞检測中onContactBegin回调函数不响应问题

如何检查两个Box2d机构是否在任何时刻发生碰撞/重叠?

Cocos2d / Box2d CCribbon 碰撞检测