0
\$\begingroup\$

I'll let the gif speak first. enter image description here As you can see, the small cube just kinda floats there like it has a forcefield. This weird glitch only happens with small cubes, smaller than 60 pixels for some reason. It's 32x32. And I have no clue why this happens, at all. I've debugged and debugged, but I am at my end.

Box class

//Header
class Box
{
public:
    Box(const std::string& _textureName, int _x, int _y, int _width, int _height, SDL_Renderer* _renderer, b2World* _world);

    void draw(SDL_Renderer* _render) const;
    void update();
private:
    SDL_Texture* m_image;
    SDL_Rect m_bounds{};

    b2PolygonShape m_polygon;
    b2FixtureDef m_fixture;
    b2BodyDef m_bodyDef;
    b2Body* m_body;
};

//CPP
Box::Box(const std::string& _textureName, int _x, int _y, int _width, int _height, SDL_Renderer* _renderer, b2World* _world) : m_image(IMG_LoadTexture(_renderer, _textureName.c_str()))
{
    SDL_QueryTexture(m_image, nullptr, nullptr, nullptr, nullptr);

    m_bounds.x = _x;
    m_bounds.y = _y;
    m_bounds.w = _width;
    m_bounds.h = _height;

    m_bodyDef.type = b2_dynamicBody;
    m_bodyDef.position.Set(_x / World::PPM, _y / World::PPM);
    m_body = _world->CreateBody(&m_bodyDef);

    m_polygon.SetAsBox((_width / World::PPM) / 2.0f, (_height / World::PPM) / 2.0f);

    m_fixture.shape = &m_polygon;
    m_fixture.density = 1.0f;
    m_fixture.friction = 0.3f;

    m_body->CreateFixture(&m_fixture);
}

void Box::draw(SDL_Renderer* _render) const
{
    SDL_Point center;
    center.x = m_bounds.w - (m_bounds.w / 2);
    center.y = m_bounds.h - (m_bounds.h / 2);

    const auto angle = (180 * m_body->GetAngle()) / M_PI;

    SDL_RenderCopyEx(_render, m_image, nullptr, &m_bounds, angle, &center, SDL_FLIP_NONE);
}

void Box::update()
{
    m_bounds.x = m_body->GetPosition().x * World::PPM;
    m_bounds.y = m_body->GetPosition().y * World::PPM;
}

World class

//Header
class World
{
public:
    static constexpr float PPM = 32.0f;
    static constexpr int VELOCITY_ITERATIONS = 5;
    static constexpr int POSITION_ITERATIONS = 8;

    World();

    void update();
    void render(SDL_Renderer* m_renderer);

    void createBox(const std::string& _textureName, int _x, int _y, int _width, int _height, SDL_Renderer* _renderer);
private:
    float m_timeStep;

    b2World m_world;
    b2BodyDef m_groundBodyDef;
    b2Body* m_groundBody;

    std::vector<Box*> m_boxes{};
};

//CPP
World::World() : m_world(b2Vec2(0.0f, 9.8)), m_timeStep(1.0f / 60.0f)
{
    m_groundBodyDef.position.Set(400.0f / PPM, 590.0f / PPM);
    m_groundBody = m_world.CreateBody(&m_groundBodyDef);

    b2PolygonShape groundBox;
    groundBox.SetAsBox(400.0f / PPM, 20.0f / PPM);
    m_groundBody->CreateFixture(&groundBox, 2.0f);
}

void World::update()
{
    m_world.Step(m_timeStep, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
    for (auto box : m_boxes)
    {
        box->update();
    }
}

void World::render(SDL_Renderer* m_renderer)
{
    for(auto box : m_boxes)
    {
        box->draw(m_renderer);
    }
}

void World::createBox(const std::string& _textureName, int _x, int _y, int _width, int _height, SDL_Renderer* _renderer)
{
    m_boxes.push_back(new Box(_textureName, _x, _y, _width, _height, _renderer, &m_world));
}

If anybody has any idea why this might happen, please let me know. Thank you.

\$\endgroup\$
  • \$\begingroup\$ It looks to me like your center of gravity and moment of inertia has been calculated incorrectly as attested to by the uncanny way your large box teeters but doesn't fall over. It's center of mass is clearly well over the side of the lower box, but it teeters as though it's on a much larger object. As I cannot see your polygon implementation, I can only guess. But I would have to say that the use of so many divisions by constants and reliance on x_ and y_ as both position and a scalar quantity relating to the dimensions, there appear to be some inputs mixed around. Comments wouldn't hurt, too. \$\endgroup\$ – Jon Koelzer Sep 10 at 23:36
  • \$\begingroup\$ I've added the header files. I don't use a custom polygon but Box2D's polygon shape. I don't understand what you mean using x and y for scalar, as I only use it for positions. \$\endgroup\$ – GigaNova Sep 11 at 8:35
  • \$\begingroup\$ Specifically, it is your Box() constructor and draw() function which confuse me. I do not know what PPM stands for, is that something like pixel per meter? - center.x and y, which I assume represent the world position of the box, use dimensionality instead of the previously defined calculations from your m_bodyDef position. Then before that you use the PPM variable plus division by two to set what i believe are the dimensions of your box. I'm having trouble undestanding why most of this math is happening. I believe commenting your thought process on each step of those functions will help. \$\endgroup\$ – Jon Koelzer Sep 11 at 9:27
  • \$\begingroup\$ Not sure if you have covered this somewhere else, but you appear to have a memory leak as well from your create box function. If I had to guess what the culprit was, I would say it is your use of PPM. While I don't know what it's for, it is the most confusing bit of code and incidentally the only thing I can see which includes the number 32. My intuition tells me you either need to use it everywhere and in every calculation, or get rid of it entirely. \$\endgroup\$ – Jon Koelzer Sep 11 at 9:30
  • \$\begingroup\$ PPM is Pixel Per Meter, it is used by Box2D to define how many pixels is a "real" meter. Removing it makes the objects alot more massive, but the problem persists, so it's not the culprit. Also m_bounds is for SDL2 to draw the rectangle, so it needs pixel values. \$\endgroup\$ – GigaNova Sep 11 at 9:43
1
\$\begingroup\$

Problem solved: the error was in drawing the boxes all along. The way Box2D returns positions is by returning the center of an object, and not the upper-left corner. So all I had to do was deduct half the width and height from my texture's position to draw it in the right spot.

    m_bounds.x = m_body->GetPosition().x * World::PPM;
    m_bounds.y = m_body->GetPosition().y * World::PPM;

becomes

    m_bounds.x = m_body->GetPosition().x * World::PPM - (m_bounds.w / 2.0f);
    m_bounds.y = m_body->GetPosition().y * World::PPM - (m_bounds.h / 2.0f);
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.