Box2D blocky map. Body, Fixtures a huge map and performance

Posted by Solom on Game Development See other posts from Game Development or by Solom
Published on 2014-08-18T21:55:01Z Indexed on 2014/08/18 22:33 UTC
Read the original article Hit count: 229

Filed under:
|
|

Right now I'm still in the planning phase of a my very first game. I'm creating a "Minecraft"-like game in 2D that features blocks that can be destroyed as well as players moving around the map.

For creating the map I chose a 2D-Array of Integers that represent the Block ID. For testing purposes I created a huge map (16348 * 256) and in my prototype that didn't use Box2D everything worked like a charm.

I only rendered those blocks that where within the bounds of my camera and got 60 fps straight. The problem started when I decided to use an existing physics-solution rather than implementing my own one. What I had was basically simple hitboxes around the blocks and then I had to manually check if the player collided with any of those in his neighborhood.

For more advanced physics as well as the collision detection I want to switch over to Box2D. The problem I have right now is ... how to go about the bodies?

I mean, the blocks are of a static bodytype. They don't move on their own, they just are there to be collided with. But as far as I can see it, every block needs his own body with a rectangular fixture attached to it, so as to be destroyable.

But for a huge map such as mine, this turns out to be a real performance bottle-neck. (In fact even a rather small map [compared to the other] of 1024*256 is unplayable.) I mean I create thousands of thousands of blocks. Even if I just render those that are in my immediate neighborhood there are hundreds of them and (at least with the debugRenderer) I drop to 1 fps really quickly (on my own "monster machine").

I thought about strategies like creating just one body, attaching multiple fixtures and only if a fixture got hit, separate it from the body, create a new one and destroy it, but this didn't turn out quite as successful as hoped. (In fact the core just dumps. Ah hello C! I really missed you :X)

Here is the code:

public class Box2DGameScreen implements Screen
{
    private World world;
    private Box2DDebugRenderer debugRenderer;
    private OrthographicCamera camera;

    private final float TIMESTEP = 1 / 60f; // 1/60 of a second -> 1 frame per second
    private final int VELOCITYITERATIONS = 8;
    private final int POSITIONITERATIONS = 3;

    private Map map;
    private BodyDef blockBodyDef;
    private FixtureDef blockFixtureDef;

    private BodyDef groundDef;
    private Body ground;

    private PolygonShape rectangleShape;

    @Override
    public void show()
    {
        world = new World(new Vector2(0, -9.81f), true);
        debugRenderer = new Box2DDebugRenderer();
        camera = new OrthographicCamera();
        // Pixel:Meter = 16:1

        // Body definition
        BodyDef ballDef = new BodyDef();
        ballDef.type = BodyDef.BodyType.DynamicBody;
        ballDef.position.set(0, 1);

        // Fixture definition
        FixtureDef ballFixtureDef = new FixtureDef();
        ballFixtureDef.shape = new CircleShape();
        ballFixtureDef.shape.setRadius(.5f); // 0,5 meter
        ballFixtureDef.restitution = 0.75f; // between 0 (not jumping up at all) and 1 (jumping up the same amount as it fell down)
        ballFixtureDef.density = 2.5f; // kg / m²
        ballFixtureDef.friction = 0.25f; // between 0 (sliding like ice) and 1 (not sliding)

    //    world.createBody(ballDef).createFixture(ballFixtureDef);

        groundDef = new BodyDef();
        groundDef.type = BodyDef.BodyType.StaticBody;
        groundDef.position.set(0, 0);

        ground = world.createBody(groundDef);

        this.map = new Map(20, 20);

        rectangleShape = new PolygonShape();
    //    rectangleShape.setAsBox(1, 1);

        blockFixtureDef = new FixtureDef();
    //    blockFixtureDef.shape = rectangleShape;
        blockFixtureDef.restitution = 0.1f;
        blockFixtureDef.density = 10f;
        blockFixtureDef.friction = 0.9f;
    }

    @Override
    public void render(float delta)
    {
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);


        debugRenderer.render(world, camera.combined);
        drawMap();

        world.step(TIMESTEP, VELOCITYITERATIONS, POSITIONITERATIONS);
    }

    private void drawMap()
    {

        for(int a = 0; a < map.getHeight(); a++)
        {
        /*
            if(camera.position.y - (camera.viewportHeight/2) > a)
                continue;

            if(camera.position.y - (camera.viewportHeight/2) < a)
                break;
        */
            for(int b = 0; b < map.getWidth(); b++)
            {
            /*    if(camera.position.x - (camera.viewportWidth/2) > b)
                    continue;

                if(camera.position.x - (camera.viewportWidth/2) < b)
                    break;
            */
            /*
                blockBodyDef = new BodyDef();
                blockBodyDef.type = BodyDef.BodyType.StaticBody;
                blockBodyDef.position.set(b, a);

                world.createBody(blockBodyDef).createFixture(blockFixtureDef);
            */
                PolygonShape rectangleShape = new PolygonShape();
                rectangleShape.setAsBox(1, 1, new Vector2(b, a), 0);

                blockFixtureDef.shape = rectangleShape;

                ground.createFixture(blockFixtureDef);
                rectangleShape.dispose();

            }
        }
    }

    @Override
    public void resize(int width, int height)
    {
        camera.viewportWidth = width / 16;
        camera.viewportHeight = height / 16;
        camera.update();
    }

    @Override
    public void hide() {
        dispose();
    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void dispose() {
        world.dispose();
        debugRenderer.dispose();
    }
}

As you can see I'm facing multiple problems here. I'm not quite sure how to check for the bounds but also if the map is bigger than 24*24 like 1024*256 Java just crashes -.-. And with 24*24 I get like 9 fps. So I'm doing something really terrible here, it seems and I assume that there most be a (much more performant) way, even with Box2D's awesome physics.

Any other ideas?

Thanks in advance!

© Game Development or respective owner

Related posts about libgdx

Related posts about box2d