0
\$\begingroup\$

I'm using SFML and Box2D to create an application that spawns circles, triangles and rectangles at the current mouse point when the user presses the C, T, and R keys. The problem is that the shapes don't move even though the position of the graphic is constantly updated with the position of the physics body. I've looked at the code of working examples of similar setups and can't find any obvious explanation for why it isn't working.

Here is the complete code.

Shape.h

#include <Box2D/Box2D.h>

class Shape
{
public:
    Shape(float x, float y) : position(x, y) {};

    void setAsCircle(float radius);
    void setAsTriangle(float base, float height);
    void setAsRectangle(float width, float height);

    void createBody(b2World &world, bool dynamic = true);
    sf::Shape &getShape() { return *shape; };
    void update();

private:
    sf::Vector2f position;  
    sf::Shape* shape;
    b2Body* body;

    const int worldScale = 30;
};

Shape.cpp

#include "Shape.h"

void Shape::setAsCircle(float radius)
{
    sf::CircleShape *circle = new sf::CircleShape(radius);
    circle->setPosition(position);
    circle->setOrigin(radius, radius);
    shape = circle;
}

void Shape::setAsTriangle(float base, float height)
{
    sf::ConvexShape *triangle = new sf::ConvexShape(3);
    triangle->setPoint(0, sf::Vector2f(0, height));
    triangle->setPoint(1, sf::Vector2f(base / 2, 0));
    triangle->setPoint(2, sf::Vector2f(base, height));
    triangle->setPosition(position);
    triangle->setOrigin(base / 2, height / 2);
    shape = triangle;
}

void Shape::setAsRectangle(float width, float height)
{
    sf::RectangleShape *rectangle = new sf::RectangleShape(sf::Vector2f(width, height));
    rectangle->setPosition(position);
    rectangle->setOrigin(width / 2, height / 2);
    shape = rectangle;
}

void Shape::createBody(b2World &world, bool dynamic)
{
    b2BodyDef bodyDef;
    if (dynamic) bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(position.x / worldScale, position.y / worldScale);
    body = world.CreateBody(&bodyDef);

    b2PolygonShape polygonShape;
    polygonShape.SetAsBox(shape->getLocalBounds().width / 2 / worldScale, shape->getLocalBounds().height / 2 / worldScale);

    b2FixtureDef fixtureDef;
    fixtureDef.shape = &polygonShape;
    fixtureDef.density = dynamic;
    fixtureDef.friction = 0.3f;
    body->CreateFixture(&fixtureDef);
}

void Shape::update()
{
    shape->setPosition(body->GetPosition().x * worldScale, body->GetPosition().y * worldScale);
    shape->setRotation(body->GetAngle() * 180 / b2_pi);
}

App.h

#pragma once

#include "Shape.h"
#include <memory>
#include <random>

class App
{
public:
    App();
    App(const App&) : world(b2Vec2(0, -10)) {};

private:
    void events();
    void update();
    void render();
    void handleKeyPress(int key);

    sf::RenderWindow window;
    std::vector<Shape> shapes;
    b2World world;

    const int windowWidth = 1024, windowHeight = 768;
};

App.cpp

#include "App.h"

App::App() : world(b2Vec2(0, -10))
{
    window.create(sf::VideoMode(windowWidth, windowHeight), "sf2D physics sandbox");

    Shape shape(0, static_cast<float>(windowHeight));
    shape.setAsRectangle(static_cast<float>(windowWidth), 0);
    shape.createBody(world, false);
    shapes.push_back(shape);

    while (window.isOpen())
    {
        events();
        update();
        render();
    }
}

void App::events()
{
    sf::Event event;
    while (window.pollEvent(event))
    {
        switch (event.type)
        {
        case sf::Event::Closed:
            window.close();
            break;

        case sf::Event::KeyPressed:
            handleKeyPress(event.key.code);
            break;
        }
    }
}

void App::update()
{
    world.Step(1 / 60, 8, 3);
    for (Shape &shape : shapes) shape.update();
}

void App::render()
{
    window.clear();
    for (Shape &shape : shapes) window.draw(shape.getShape());
    window.display();
}

void App::handleKeyPress(int key)
{
    float mouseX = static_cast<float>(sf::Mouse::getPosition(window).x);
    float mouseY = static_cast<float>(sf::Mouse::getPosition(window).y);
    sf::Color randomColor = sf::Color(rand() % 256 + 0, rand() % 256 + 0, rand() % 256 + 0);

    Shape shape(mouseX, mouseY);
    switch (key)
    {
    case sf::Keyboard::C:
        shape.setAsCircle(rand() % 100 + 25);
        break;

    case sf::Keyboard::T:
        shape.setAsTriangle(rand() % 100 + 25, rand() % 100 + 25);
        break;

    case sf::Keyboard::R:
        shape.setAsRectangle(rand() % 100 + 25, rand() % 100 + 25);
        break;
    }
    shape.createBody(world);

    shapes.push_back(shape);
}

main.cpp

#include "App.h"

int main()
{
    App app = App();

    return 0;
}

The position of the shapes do not change after they are created. Where have I gone wrong?

\$\endgroup\$
  • \$\begingroup\$ I do not understand your problem. \$\endgroup\$ – jgallant Oct 19 '16 at 15:39
  • \$\begingroup\$ I want the shapes to be physics objects that react to the pull of gravity. My problem is that they simply stay in place and do not move as I would like them to. \$\endgroup\$ – Greg Treleaven Oct 19 '16 at 15:43
1
\$\begingroup\$

Eventually I found the answer myself. The problem was with this line:

world.Step(1 / 60, 8, 3);

where 60 was considered an integer so the division rounded out to zero. This caused the world to simulate at a speed of zero, which explains why nothing happened.

I modified it as such:

world.Step(1 / 60.0f, 8, 3);

so that 60 was correctly assessed as a float. After some minor adjustments everything now works as expected.

\$\endgroup\$
  • \$\begingroup\$ Don't forget to mark your answer as "Accepted" so others with similar issues can see this highlighted as a solved question when searching. \$\endgroup\$ – DMGregory Nov 1 '16 at 22:11

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.