Box2D Problem

Started by
5 comments, last by TheStudent111 8 years, 6 months ago

I'm having trouble with ApplyForce. I have rendered a simple title world, where the user gets dropped into. Whenever I use one of my arrows keys to move the character by using ApplyForce, it works for a few seconds then stops. Debugging shows that the LinearVelocity gets set to zero, despite the fact that the arrow is being pressed, and a force is being applied. I don't know what the issue is, I'm fairly new to Box2D.

Here is some sample code


while(Window.isOpen())
{
    for(b2Body * BodyIterator = World.GetBodyList(); BodyIterator != 0; BodyIterator = BodyIterator->GetNext())

        {    



             if(BodyIterator->GetType() == b2_dynamicBody)

            {

                

                Player.setPosition(30.0f * BodyIterator->GetPosition().x, BodyIterator->GetPosition().y * 30.0f);

                



                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))

                {

                    Body->ApplyForce(b2Vec2(5.0f, 0.0f), b2Vec2(0.0f, 0.0f), true);

                    std::cout << "Pressed" << std::endl;

                }

                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))

                {

                    Body->ApplyForce(b2Vec2(-5.0f, 0.0f), b2Vec2(0.0f, 0.0f), true);



                }



                std::cout << Body->GetLinearVelocity().x << std::endl;

                Window.draw(Player);



            }

             else if(BodyIterator->GetType() == b2_staticBody)

            {        

                for(int i = 0; i < MAP_HEIGHT; i++)

                {

                    for(int j = 0; j < MAP_WIDTH; j++)

                    {

                        

                        if(MAP[i][j] == 1)

                        {    

                            

                            Ground.setPosition(BodyIterator->GetPosition().x * 30.0f, BodyIterator->GetPosition().y * 30.0f);



                            Window.draw(Ground);                    

                        }

                        

                    }



                }



            }

        

        }

       }
 
 

Not sure if above is enough. Will post more if requested.

Advertisement

Assuming your SetPosition is doing a position.Set() behind the scenes, You really shouldn't be intermingling ApplyForce and SetPosition(). While I couldn't find anything explicit, Set() essentially bypasses the physic engine entirely and just relocates the given object to the new location. It wouldn't surprise me if that is also resetting the physics state for that object as well.

Use SetPosition once outside your while loop to initially place all objects in the scene, then let ApplyForce do it's thing. See if that resolves the issue, or at least improves it.

I realize now that's the SFML SetPosition.

Ah... just noticed you aren't 'Step'ping your simulation anywhere in that code, and that looks like your main loop. b2World.Step() is where all the simulating actually happens. If you don't call that, nothing gets simulated.

I am stepping


while(Window.isOpen())
    {
        Window.clear();
        sf::Event Event;
    
        while(Window.pollEvent(Event))
        {
            switch(Event.type)
            {
            case sf::Event::Closed:
                Window.close();
                break;
            
            case sf::Event::KeyPressed:
                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
                    Window.close();
                break;
            
            }

        
        }

        World.Step((1/60.0f), 8, 3);     // <-------

        for(b2Body * BodyIterator = World.GetBodyList(); BodyIterator != 0; BodyIterator = BodyIterator->GetNext())
        {    

             if(BodyIterator->GetType() == b2_dynamicBody)
            {
                
                Player.setPosition(30.0f * BodyIterator->GetPosition().x, BodyIterator->GetPosition().y * 30.0f);
                

                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
                {
                    Body->ApplyForce(b2Vec2(5.0f, 0.0f), b2Vec2(0.0f, 0.0f), true);
                    std::cout << "Pressed" << std::endl;
                }
                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
                {
                    Body->ApplyForce(b2Vec2(-5.0f, 0.0f), b2Vec2(0.0f, 0.0f), true);
                    std::cout << "Pressed" << std::endl;
                }

                std::cout << Body->GetLinearVelocity().x << std::endl;
                Window.draw(Player);

            }
             else if(BodyIterator->GetType() == b2_staticBody)
            {        
                Ground.setPosition(30.0f * BodyIterator->GetPosition().x, 30.0f * BodyIterator->GetPosition().y);
                Window.draw(Ground);
            }
        
        }
        


        Window.display();
    }
 
 

I think I know what the problem is but I'm not a 100% sure. I think its because my platforms are tiled, are composed of squares or sf::Rectangles that are 20 by 20. What I did is I created another program that has 1 continuous platform (one big sf::rectangle), that covers the all screen and acts as the ground. So far I haven't had any problems moving the character. So is a tiled platform design a good idea when using Box2D?


    const int MAP_WIDTH = 40;
    const int MAP_HEIGHT = 20;

    int MAP[MAP_HEIGHT][MAP_WIDTH] =
    {
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    };

    const int TILE_SIZE = 20;
    const int FLOOR_SIZE = 50;

    sf::RectangleShape Ground;
    Ground.setSize(sf::Vector2f(TILE_SIZE, TILE_SIZE));
    Ground.setOrigin(20.0f, 10.0f);
    Ground.setFillColor(sf::Color::Red);

    sf::RectangleShape Player;
    Player.setSize(sf::Vector2f(10.0f, 20.0f));
    Player.setOrigin(5.0f, 10.0f);
    Player.setPosition(0.0f, 10.0f);
    Player.setFillColor(sf::Color::White);

    for(int i = 0; i < MAP_HEIGHT; i++)
    {
        for(int j = 0; j < MAP_WIDTH; j++)
        {
            if(MAP[i][j] == 1)
            {
                CreateGround(World, (j * TILE_SIZE), (i * TILE_SIZE));
            }
        
        }

    }
 
    b2Body * Body = nullptr;
    b2BodyDef BodyDef;
    BodyDef.position = b2Vec2(Player.getPosition().x / 30.0f, Player.getPosition().y / 30.0f);
    BodyDef.type = b2_dynamicBody;

    Body = World.CreateBody(&BodyDef);

    b2PolygonShape Shape;
    Shape.SetAsBox((10.0f / 2) / 30.0f, (20.0f / 2) / 30.0f);

    b2FixtureDef FixtureDef;
    FixtureDef.friction = 0.5;
    FixtureDef.shape = &Shape;

    Body->CreateFixture(&FixtureDef);
 
// Followed by the while loop above

void CreateGround(b2World & World, float x, float y)
{
    b2BodyDef BodyDef;
    BodyDef.position = b2Vec2(x / 30.0f, y / 30.0f);
    BodyDef.type = b2_staticBody;
    

    b2Body * Body = World.CreateBody(&BodyDef);

    b2PolygonShape Shape;
    Shape.SetAsBox((20.0f / 2) / 30.0f,  (20.0f / 2) / 30.0f);
    
    b2FixtureDef FixtureDef;
    FixtureDef.density = 0.0;
    FixtureDef.shape = &Shape;

    Body->CreateFixture(&FixtureDef);
}
 


So is a tiled platform design a good idea when using Box2D?

It's definitely a potential source of issues. See this wonderful tutorial on the subject: http://www.iforce2d.net/b2dtut/ghost-vertices

It sounds like it might be the ghost vertices. Your OP is not fully clear to me, but i'll assume your body stops moving suddenly and doesn't move at all in that direction anymore? Does it move in the other direction?

What you could do is generate the static bodies a bit differently. Instead of making them one at a time and having a large amount of small boxes, generate as big of a box as you can in the x direction. Modify the algorithm so when you find a tile that is == 1 (aka ground), find all consecutive tiles on the x axis that are == 1, and if you find there are i.e. 5 of them (looking at your map definition above), you would generate a box that is 5 times the width of a single box, with proper position modification so it gets placed in the right location. You could also modify teh algorithm so you look at both x and y directions, so if you have a block of ground that is 3 high and 5 long, you generate just one box with the proper position and size instead of 3 when just looking at the x axis, and opposed to 15 with your current method.

devstropo.blogspot.com - Random stuff about my gamedev hobby

Thanks @jHaskell for the help. I had some suspicion that was the problem.

@Strewya, in my original post, I meant that the player stops moving in one direction(to the right), then when the user tries to move to the left the block moves for a few seconds then stops. The LinearVelocity is set to zero again when the character tries to move in the opposite direction. So there is movement but only for a few seconds, then the LinearVelocity is set to zero gain for both directions.

I really love your suggestion on how to improve my algorithm. I might just use it, thanks.

This topic is closed to new replies.

Advertisement