Box2D создает прямоугольные ограничительные рамки вокруг угловых линий тел - PullRequest
2 голосов
/ 10 июня 2011

У меня много проблем с обнаружением столкновений в космической игре в невесомости. Надеюсь, это изображение поможет мне объяснить:

http://i.stack.imgur.com/f7AHO.png

Белый прямоугольник - это статическое тело с прикрепленным креплением b2PolygonShape, как таковое:

    // Create the line physics body definition
b2BodyDef wallBodyDef;
wallBodyDef.position.Set(0.0f, 0.0f);

// Create the line physics body in the physics world
wallBodyDef.type = b2_staticBody; // Set as a static body
m_Body = world->CreateBody(&wallBodyDef);

    // Create the vertex array which will be used to make the physics shape
b2Vec2 vertices[4];
vertices[0].Set(m_Point1.x, m_Point1.y); // Point 1
vertices[1].Set(m_Point1.x + (sin(angle - 90*(float)DEG_TO_RAD)*m_Thickness), m_Point1.y - (cos(angle - 90*(float)DEG_TO_RAD)*m_Thickness)); // Point 2
vertices[2].Set(m_Point2.x + (sin(angle - 90*(float)DEG_TO_RAD)*m_Thickness), m_Point2.y - (cos(angle - 90*(float)DEG_TO_RAD)*m_Thickness)); // Point 3
vertices[3].Set(m_Point2.x, m_Point2.y); // Point 3
int32 count = 4; // Vertex count

b2PolygonShape wallShape; // Create the line physics shape
wallShape.Set(vertices, count); // Set the physics shape using the vertex array above

// Define the dynamic body fixture
b2FixtureDef fixtureDef;
fixtureDef.shape = &wallShape; // Set the line shape
fixtureDef.density = 0.0f; // Set the density
fixtureDef.friction = 0.0f; // Set the friction
fixtureDef.restitution = 0.5f; // Set the restitution

// Add the shape to the body
m_Fixture = m_Body->CreateFixture(&fixtureDef);
m_Fixture->SetUserData("Wall");[/code]

Вы должны поверить мне, что это создает форму на изображении. Физическая симуляция работает отлично, игрок (маленький треугольник) сталкивается с телом с точностью до пикселя. Тем не менее, я сталкиваюсь с проблемой, когда пытаюсь определить, когда происходит столкновение, чтобы я мог убрать здоровье, а что нет. Код, который я использую для этого, выглядит следующим образом:

/*------ Check for collisions ------*/
        if (m_Physics->GetWorld()->GetContactCount() > 0)
        {
            if (m_Physics->GetWorld()->GetContactList()->GetFixtureA()->GetUserData() == "Player" &&
                m_Physics->GetWorld()->GetContactList()->GetFixtureB()->GetUserData() == "Wall")
            {
                m_Player->CollideWall();
            }
        }

Я знаю, что, возможно, есть более эффективные способы создания коллизий, но я всего лишь новичок и нигде не нашел такого объяснения, как правильно выполнять слушатели и обратные вызовы, чтобы я мог их понять. У меня проблема в том, что GetContactCount () показывает контакт всякий раз, когда тело игрока входит в фиолетовую рамку выше. Очевидно, создается прямоугольный ограничивающий прямоугольник, который охватывает белый прямоугольник.

Я пытался сделать прибор EdgeShape, и происходит то же самое. Кто-нибудь знает, что здесь происходит? Мне бы очень хотелось, чтобы столкновение было пригвождено, чтобы я мог перейти к другим вещам. Большое спасибо за любую помощь.

Ответы [ 4 ]

2 голосов
/ 10 июня 2011

Создание прослушивателя контактов не очень сложно, из документов (добавлено, чтобы попытаться справиться с вашей ситуацией):

class MyContactListener:public b2ContactListener
{
private:
    PlayerClass *m_Player;

public:
    MyContactListener(PlayerClass *player) : m_Player(player)
    { }

    void BeginContact(b2Contact* contact)
    { /* handle begin event */ }

    void EndContact(b2Contact* contact)
    {
        if (contact->GetFixtureA()->GetUserData() == m_Player
         || contact->GetFixtureB()->GetUserData() == m_Player)
        {
            m_Player->CollideWall();
        }
    }

    /* we're not interested in these for the time being */

    void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
    { /* handle pre-solve event */ }

    void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
    { /* handle post-solve event */ }
};

Для этого необходимо назначить m_Player в поле пользовательских данных прибора игрока,Тогда вы можете использовать контактный слушатель так:

m_Physics->GetWorld()->SetContactListener(new MyContactListener(m_Player));
2 голосов
/ 10 июня 2011

Ограничивающий прямоугольник - это AABB (ограничивающий прямоугольник, ориентированный по оси), что означает, что он всегда будет выровнен по декартовым осям.AABB обычно используются для обнаружения столкновений в широкой фазе, потому что это относительно простые (и недорогие) вычисления.

Вам необходимо убедиться, что вы проверяете по OBB (ориентированному ограничивающему прямоугольнику) для объектов, если хотите большеточные (но не идеальные по пикселям, как указал Мика) результаты.

Кроме того, я согласен с ответом Мики о том, что вам, скорее всего, понадобится более общая система для обработки столкновений.Даже если у вас есть только стены и игрок, нет никакой гарантии, что какой-то объект будет A, а какой - B. И когда вы добавите другие типы объектов, это быстро распадется.

0 голосов
/ 10 июня 2011

В Box2D наличие контакта просто означает, что AABB двух приборов перекрываются. Это не обязательно означает, что формы самих приборов соприкасаются.

Вы можете использовать функцию IsTouching () контакта, чтобы проверить, касаются ли фигуры на самом деле, но предпочтительным способом борьбы со столкновениями является использование функции обратного вызова, чтобы двигатель сообщал вам, когда два прибора начинают / останавливают касание , Использование обратных вызовов намного эффективнее и проще в долгосрочной перспективе, хотя при первоначальной настройке может потребоваться немного больше усилий, и есть несколько моментов, которые следует соблюдать осторожность - см. Здесь пример: http://www.iforce2d.net/b2dtut/collision-callbacks

0 голосов
/ 10 июня 2011

Откуда вы знаете, GetFixtureA - игрок, а B - стена? Это могло быть полностью изменено? Может ли быть FixtureC? Я думаю, вам понадобится более общее решение.

Я использовал похожую графическую среду (Qt), и в ней было что-то, чтобы вы могли захватывать любые два объекта и вызывать что-то вроде hasCollided, которое будет возвращать логическое значение. Вы можете избежать использования обратного вызова и просто вызвать его в drawScene () или периодически проверять его.

...