Hello everyone!
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 stared 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 neighbourhood 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(b, a, 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 2424 like 1024256 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!