Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Yet another Box2D Positioning Issue  (Read 8256 times)

0 Members and 1 Guest are viewing this topic.

wilrader

  • Newbie
  • *
  • Posts: 5
    • View Profile
Yet another Box2D Positioning Issue
« on: May 27, 2010, 10:52:47 am »
It seems that all the threads I could find on this were code-less and were eventually solved without explaining HOW they were solved. I'm attempting to get Box2D and SFML to play nicely together, and they do seem to for the most part, but I'm having a TINY issue in getting everything to line up as they should. My box object likes to hover about 9 pixels above my ground object and I can't seem to figure out why.

Code: [Select]
#include <SFML/Graphics.hpp>

// Box2D Header
#include <Box2D/Box2D.h>

int main() {

    // Create the SFML Window
sf::RenderWindow Game(sf::VideoMode(800, 600, 32), "SFML Blank Window");
Game.SetFramerateLimit(60);

    //Create the SFML Event handler
    sf::Event Event;

    // Create the SFLM shape
    sf::Shape box = sf::Shape::Rectangle(0, 0, 50, 50, sf::Color(127, 0, 0, 255));
    //box.SetCenter(25, 25);
    sf::Shape ground = sf::Shape::Rectangle(0, 0, 800, 10, sf::Color(0, 127, 0, 255));
    //box.SetCenter(400, 5);

    // Set the box position
    box.SetPosition(100, 100);
    ground.SetPosition(0, 580);

    // Create the image buffer
    sf::Image image;
    if (!image.LoadFromFile("box.jpg"))
        return EXIT_FAILURE;

    // Create the SFML sprite
    sf::Sprite sprite;

    // Set the sprites image
    sprite.SetImage(image);

    // Set up world properties
    bool doSleep = true;
    b2Vec2 gravity(0, 10.0f);
    int iterations = 10;
    float scale = 30;
    float timeStep = 1.0f / 60.0f;

    // Create the world
    b2World world(gravity, doSleep);

    // Set up box properties
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(100/scale, 100/scale);
    b2Body* body = world.CreateBody(&bodyDef);
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox(50/scale, 50/scale);
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    body->CreateFixture(&fixtureDef);

    //body->SetTransform(body->GetPosition(), 10);

    // Create ground
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0/scale, 580/scale);
    b2Body* groundBody = world.CreateBody(&groundBodyDef);
    b2PolygonShape groundBox;
    groundBox.SetAsBox(800/scale, 10/scale);
    b2FixtureDef groundFixDef;
    groundFixDef.shape = &groundBox;
    groundFixDef.restitution = 0.5f;
    groundBody->CreateFixture(&groundFixDef);

    while (Game.IsOpened()) {
        while (Game.GetEvent(Event)) {
            if (Event.Type == sf::Event::Closed)
                Game.Close();
        }

        world.Step(timeStep, 10, 1);
        world.ClearForces();

        b2Vec2 pos = body->GetPosition();
        float32 angle = body->GetAngle();

        box.SetPosition(pos.x*scale, pos.y*scale);
        box.SetRotation(-(angle*(180/b2_pi)));

        Game.Clear();

        Game.Draw(sprite);
        Game.Draw(box);
        Game.Draw(ground);

        Game.Display();
    }

    return EXIT_SUCCESS;
}


Any help would be appreciated. Thank you!

panithadrum

  • Sr. Member
  • ****
  • Posts: 304
    • View Profile
    • Skyrpex@Github
    • Email
Yet another Box2D Positioning Issue
« Reply #1 on: May 27, 2010, 12:55:55 pm »
I had positioning problems until I noticed that:
1 - Box2D bodies are centered to the middle of the shape.
2 - SFML shapes are centered to (0, 0) by default.
3 - When I create a body using SetAsBox(width, height) you must know that the final body will have the size of (width*2, height*2).

Let me write an example:
Code: [Select]
// Define how many pixels a meter have
#define PIXELS_METER 30.f

// World: valid world
// Body: this will be the output body
// Shape: this will be the output shape
// Position: position of the body in pixels
// Size: size of the body (rectangle) in pixels
void CreateBody(b2World *World, b2Body *Body, sf::Shape &Shape, const sf::Vector2f &Position, const sf::Vector2f &Size)
{
// Create the body
{
b2BodyDef def;
def.position.Set(Position.x/PIXELS_METER, Position.y/PIXELS_METER);
Body = World->CreateBody(&def);
}
{
b2PolygonShape pol;
pol.SetAsBox(Size.x/PIXELS_METER, Size.y/PIXELS_METER);
Body->CreateFixture(&pol, 1.f);
}
// Create the shape
{
Shape = sf::Shape::Rectangle(0.f, 0.f, Size.x*2, Size.y*2, sf::Color::White);
Shape.SetOrigin(Size);
Shape.SetPosition(Position);
}
}

// This just synchronizes the shape with the body
void Synchronize(b2Body *Body, sf::Shape &Shape)
{
Shape.SetRotation(-rad_to_deg(Body->GetAngle()));
Shape.SetPosition(Body->GetPosition().x*PIXELS_METER, Body->GetPosition().y*PIXELS_METER);
}

void main()
{
// Create the window
sf::RenderWindow *Window = new sf::RenderWindow(...);
Window->Create(...);

// Create the world
b2Vec2 Gravity(0.f, 0.f);
b2World *World = new World(Gravity, true);

// Create the body and the shape
b2Body *Body;
sf::Shape Shape;
CreateBody(World, Body, Shape, sf::Vector2f(100.f, 100.f), sf::Vector2f(150.f, 150.f));

// Main loop
while(Window->IsOpened())
{
// Events
while(Window->GetEvent(...))
{
}
// Logic
World->Step(...);
Synchronize(Body, Shape);
// Graphic
Window->Clear();
Window->Draw(Shape);
Window->Display();
}
}


This is how I work with it, more or less. Note that it's not complete. Just the minimal code so you know what to do. Ask me if you have questions!

Edit: Defining pixels per meter, this way you can create bodies and shapes with constant size in pixels, not matter how many meters they represent. This is kinda a bad thing: consider doing the inverse method.

wilrader

  • Newbie
  • *
  • Posts: 5
    • View Profile
Yet another Box2D Positioning Issue
« Reply #2 on: May 27, 2010, 10:06:12 pm »
Thank you very much! I had to work with your code a little to get it to work (fought with a segmentation fault for about an HOUR lol), but now everything lines up and looks great! I'll go ahead and post the working code so that those who have trouble with this same thing in the future at least have a working program to start with. :)

Code: [Select]
#include <SFML/Graphics.hpp>

// Box2D Header
#include <Box2D/Box2D.h>

#define PIXELS_METER 30.f
#define PI 3.1415926

double degreeToRadian(double degree) {
        double radian = 0;
        radian = degree * (PI/180);
        return radian;
}

double radianToDegree(double radian) {
        double degree = 0;
        degree = radian * (180/PI);
        return degree;
}

b2Body * CreateBody(b2World *World, b2BodyType type, sf::Shape &Shape, const sf::Vector2f &Position, const sf::Vector2f &Size)
{
   // Create the body
      b2BodyDef def;
      def.type = type;
      def.position.Set(Position.x/PIXELS_METER, Position.y/PIXELS_METER);
      b2Body* Body = World->CreateBody(&def);
      b2PolygonShape pol;
      pol.SetAsBox(Size.x/PIXELS_METER, Size.y/PIXELS_METER);
      b2FixtureDef fix;
      fix.shape = &pol;
      fix.density = 1.0f;
      fix.friction = 0.3f;
      Body->CreateFixture(&fix);
      Shape = sf::Shape::Rectangle(0.f, 0.f, Size.x*2, Size.y*2, sf::Color::White);
      Shape.SetCenter(Size);
      Shape.SetPosition(Position);

      return Body;
}

void Synchronize(b2Body *Body, sf::Shape &Shape)
{
   Shape.SetRotation(-radianToDegree(Body->GetAngle()));
   Shape.SetPosition(Body->GetPosition().x*PIXELS_METER, Body->GetPosition().y*PIXELS_METER);
}

int main() {

    // Create the SFML Window
sf::RenderWindow Game(sf::VideoMode(800, 600, 32), "SFML Blank Window");
sf::Event Event;
Game.SetFramerateLimit(60);

    // Create the world
    b2Vec2 Gravity(0.f, 10.f);
    b2World World(Gravity, true);

    // Create the body and the shape
    sf::Shape Shape;
    b2Body *Body = CreateBody(&World, b2_dynamicBody, Shape, sf::Vector2f(100.f, 100.f), sf::Vector2f(50.f, 50.f));

    //Body->SetTransform(Body->GetPosition(), 45);

    sf::Shape ground;
    b2Body *groundBody = CreateBody(&World, b2_staticBody, ground, sf::Vector2f(10.f, 550.f), sf::Vector2f(800.f, 10.f));

    while (Game.IsOpened()) {
        while (Game.GetEvent(Event)) {
            if (Event.Type == sf::Event::Closed)
                Game.Close();
        }

        World.Step(1.f/60.f, 10, 1);

        Synchronize(Body, Shape);
        Synchronize(groundBody, ground);

        Game.Clear();
        Game.Draw(Shape);
        Game.Draw(ground);
        Game.Display();
    }

    return EXIT_SUCCESS;
}

panithadrum

  • Sr. Member
  • ****
  • Posts: 304
    • View Profile
    • Skyrpex@Github
    • Email
Yet another Box2D Positioning Issue
« Reply #3 on: May 27, 2010, 10:10:07 pm »
I'm sorry about the code. It was kinda pseudo-code (I didn't remember all the functions and it's arguments!).

I'm very glad you finally got it working.

By the way, why do you pass 1.f/60.f as time to the world, and not Window::GetFrameTime()?

Edit: I had trouble getting this to work with circle shapes, but finally found the solution.
Code: [Select]
sf::Vector2f Position(...);
float Radius = ...;

// Creating the box2d circle shape (size in pixels)
b2CircleShape circle;
circle.m_p.Set(Position.x/PIXELS_METER, Position.y/PIXELS_METER);
circle.m_radius = Radius/PIXELS_METER;
Body->myBody->CreateFixture(&circle);

// Creating the shape
sf::Shape::Circle(Position, Radius, sf::Color(...));

wilrader

  • Newbie
  • *
  • Posts: 5
    • View Profile
Yet another Box2D Positioning Issue
« Reply #4 on: May 27, 2010, 10:41:59 pm »
The reason was because I didn't know about GetFrameTime() haha. That works much better without having to limit the FPS.

Kordman916

  • Newbie
  • *
  • Posts: 9
    • View Profile
Yet another Box2D Positioning Issue
« Reply #5 on: December 09, 2010, 08:37:49 pm »
After running the code and making a couple changes I was wondering...

How would I replace the white box with an image?

Terrydil

  • Jr. Member
  • **
  • Posts: 51
    • View Profile
Yet another Box2D Positioning Issue
« Reply #6 on: December 10, 2010, 04:48:52 am »
Have you tried replacing sf::Shape::Rectangle with an sf::Sprite?

heishe

  • Full Member
  • ***
  • Posts: 121
    • View Profile
Yet another Box2D Positioning Issue
« Reply #7 on: December 10, 2010, 01:44:14 pm »
Quote from: "Kordman916"
After running the code and making a couple changes I was wondering...

How would I replace the white box with an image?


for rectangles, you can simply use sprites, for more complicated shapes, there's no way to do that yet since SFML doesn't support textured shapes.

You'd have to create an image file that specifically looks like the shape (lets say a rock), then use it with a sprite and lay it over the box2d shape. this way, it's going to be an actual rectangle, but look and behave like the shape you desire.

Kordman916

  • Newbie
  • *
  • Posts: 9
    • View Profile
Yet another Box2D Positioning Issue
« Reply #8 on: December 16, 2010, 01:39:14 am »
I can't figure out how to replace the box with an image of a crate I've been at it for days I just can't get it.

Maybe someone can explain it to me in better detail?

Kordman916

  • Newbie
  • *
  • Posts: 9
    • View Profile
Yet another Box2D Positioning Issue
« Reply #9 on: December 21, 2010, 11:43:53 pm »
Anyone, anyone at all.....

panithadrum

  • Sr. Member
  • ****
  • Posts: 304
    • View Profile
    • Skyrpex@Github
    • Email
Yet another Box2D Positioning Issue
« Reply #10 on: December 22, 2010, 12:54:37 pm »
Ok, so you just want to replace a rectangle with a sprite, right? Then, the body size will be the image size. The rest of the code remains the same.

Code: [Select]
sf::Image image;
sf::Sprite sprite(image);
...
b2Body *body = CreateBody(world, bodyType, sprite, position);


So, the CreateBody function will be:
Code: [Select]
b2Body * CreateBody(b2World *World, b2BodyType type, sf::Sprite &Sprite, const sf::Vector2f &Position)
{
      // Get the size of the image
      sf::Vector2f Size(Sprite.GetImage()->GetWidth(), Sprite.GetImage()->GetHeight());

   // Create the body
      b2BodyDef def;
      def.type = type;
      def.position.Set(Position.x/PIXELS_METER, Position.y/PIXELS_METER);
      b2Body* Body = World->CreateBody(&def);
      b2PolygonShape pol;
      pol.SetAsBox(Size.x/PIXELS_METER, Size.y/PIXELS_METER);
      b2FixtureDef fix;
      fix.shape = &pol;
      fix.density = 1.0f;
      fix.friction = 0.3f;
      Body->CreateFixture(&fix);
      // Shape = sf::Shape::Rectangle(0.f, 0.f, Size.x*2, Size.y*2, sf::Color::White);
      Sprite.SetCenter(Size);
      Sprite.SetPosition(Position);

      return Body;
}


I think that's all the changes we need to do... It's a pretty easy change, you should try doing something easier before getting into physics!