3
\$\begingroup\$

I'm working on a platformer and I've chosen to use Box2D for physics but I'm having trouble converting meters to pixels. I have a static object and a dynamic object. The dynamic object falls onto the static object but when the dynamic object hits the static object, they don't touch, they're about a meter apart when they're supposed to be touching.

Screenshot

As you can see from the screenshot, the dynamic object (rat) is hovering above the static object (blue rectangle) when they're supposed to be touching. I assumed it was a problem with my meters-to-pixels code so I fiddled with it and inspected variables in the debugger and still can't figure out what I'm doing wrong.

Rendering is done in pixels (via SDL2) and the origin is in the top-left corner of the window. +x is right and +y is down.

Physics is done in meters (via Box2D) and the origin is in the bottom-right corner of the window. +x is right and +y is up.

Heres the code to create the dynamic object.

b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.fixedRotation = true;

b2Body *body = world.CreateBody(&bodyDef);

b2PolygonShape shape;
shape.SetAsBox(1.0f, 1.0f);

b2FixtureDef fixtureDef;
fixtureDef.shape = &shape;
fixtureDef.density = 10.0f;

body->CreateFixture(&fixtureDef);
body->SetTransform({0.0f, 10.0f}, 0.0f);

And here's the code to create the static body

b2BodyDef bodyDef;
bodyDef.type = b2_staticBody;

b2Body *body = world.CreateBody(&bodyDef);

b2PolygonShape shape;
shape.SetAsBox(WINDOW_METER_SIZE.x, 2.0f);

b2FixtureDef fixture;
fixture.shape = &shape;
fixture.friction = 0.0f;

body->CreateFixture(&fixture);
body->SetTransform({0.0f, 0.0f}, 0.0f); 

The Rect and RectPx classes represent a rectangle as a top-left point p and a size s.

To get the Rect of the b2Body used for drawing.

b2Body *body;
const Rect rect = {body->GetPosition(), {1.0f, 1.0f}}; 

Heres the code to transform a rectangle in meters to a rectangle in pixels

RectPx transform(const Rect rect) {
  return {
    {
      rect.p.x * PIXELS_PER_METER,
      (-rect.p.y + WINDOW_METER_SIZE.y - rect.s.y) * PIXELS_PER_METER
    },
    rect.s * PIXELS_PER_METER
  };
}

I later use the RectPx to call SDL_RenderFillRect and SDL_RenderCopy which both expect a rectangle in the described format.

I really hope there's an obvious mistake in my code somewhere that a Box2D guru can spot.

Thank you in advance for reading this very long question.

\$\endgroup\$
1
\$\begingroup\$

After implementing debug draw I noticed that Box2D was rendering my objects twice the size that I was. So then I looked at the implementation of b2PolygonShape::SetAsBox. Here it is

void b2PolygonShape::SetAsBox(float32 hx, float32 hy)
{
  m_count = 4;
  m_vertices[0].Set(-hx, -hy);
  m_vertices[1].Set( hx, -hy);
  m_vertices[2].Set( hx,  hy);
  m_vertices[3].Set(-hx,  hy);
  m_normals[0].Set(0.0f, -1.0f);
  m_normals[1].Set(1.0f, 0.0f);
  m_normals[2].Set(0.0f, 1.0f);
  m_normals[3].Set(-1.0f, 0.0f);
  m_centroid.SetZero();
}

It expects half width and half height. After I passed half the size of the box to this function, I made a few tweaks to my camera code and everything worked as expected.

\$\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.