0
votes

I have created an android application similar to Not Tetris 2 using Libgdx with Box2d.

It can successfully remove a slice from the world, which obviously involves duplicating several bodies and destroying/creating fixtures. However, seemingly at random, a body with a 2x2 fixture will appear. The body and fixture are displayed using information related to the objects around it when it is created, so I narrowed its creation down to the following function:

Body duplicateBody(Body original){
    BodyDef d = new BodyDef();
    d.position.set(original.getPosition());
    d.angle = original.getAngle();
    d.linearVelocity.set(original.getLinearVelocity());
    d.angularVelocity = original.getAngularVelocity();

    Body dup = world.createBody(d);
    dup.setType(BodyDef.BodyType.DynamicBody);

    return dup;
}

I use this function in 2 different contexts:

  1. Making a copy of the body if a "slice" cuts one in two -- I then transfer the fixtures which are below to it.
  2. When a fixture is below the line then it is added to a body created for ones below
  3. Making a copy of the body when groups of fixtures are separated

I commented out the code responsible for the third instance and still had the 2x2 boxes spawning, so here are the functions relevant to the others:

...
if (below && !above) {
    //copy fixture, add copy to lower body and remove original
    Body top = fixture.getBody();
    FixtureDef n = new FixtureDef();
    PolygonShape s = new PolygonShape();
    s.set(getLocalVerticesOfFixture(fixture));
    n.shape = s;
    n.density = fixture.getDensity();
    //create lower body if a lower one doesn't already exist
    if (!topBottomPairs.containsKey(top)) {
        Body dup = duplicateBody(top);
        topBottomPairs.put(top, dup);
    }
    //delete fixture
    remove.add(fixture);
    Fixture f = topBottomPairs.get(top).createFixture(n);
    s.dispose();
}
...
if (below && above) {
    //copy fixture, add copy to lower body, but keep original on upper as it needs to split
    FixtureDef n = new FixtureDef();
    PolygonShape s = new PolygonShape();
    s.set(getLocalVerticesOfFixture(fixture));
    n.shape = s;
    n.density = fixture.getDensity();

    Body top = fixture.getBody();
    //create lower body if a lower one doesn't already exist
    if (!topBottomPairs.containsKey(top)) {
        Body dup = duplicateBody(top);
        topBottomPairs.put(top, dup);
    }
    Fixture second = topBottomPairs.get(top).createFixture(n);
    s.dispose();
}

....

private Vector2[] getLocalVerticesOfFixture(Fixture fixture) {
    PolygonShape shape = ((PolygonShape) fixture.getShape());
    Vector2[] localVertices = new Vector2[shape.getVertexCount()];
    for (int i = 0; i < shape.getVertexCount(); i++) {
        localVertices[i] = new Vector2();
        shape.getVertex(i, localVertices[i]);
    }
    return localVertices;
}

I also have this remove fixture function which runs on all fixtures I want to remove:

private void smartDeleteFixture(Fixture f){
    f.getBody().destroyFixture(f);
    if(f.getBody().getFixtureList().size == 0){
        world.destroyBody(f.getBody());
    }
}

Nowhere do I create vertices, let alone a fixture of a 2x2 shape. I was wondering if this duplication function has any flaws, or if I stumbled upon some "default shape" that box2d uses.

Edit: I have removed anything not related to the manipulation of box2d bodies. Hope that helps

1
Quite a lot going on in the code. While you've cut it down, for me, still maybe too much unknown about what it's trying to achieve. I notice your duplicatBody method looks like it has some code for a deep copy of BodyData (for userdata on body). But when you're copying fixtures, looks like multiple fixtuers point to the same fixture.userData second.setUserData(fixture.getUserData()); You using fixture user data in any special way that may account for what you're seeing?Peter R
Edited. Fixture userdata is only important when determining if fixtures were adjacent at runtime. Body userdata is only important for rendering. Problem persists if I remove the userdata, use the debug renderer, and disable things that user fixture userdataUniquePineapple
@PeterR I solved it! Completely unrelated to my code. Was due to shapes having less than 3 vertices after box2d removed ones close to each other.UniquePineapple

1 Answers

0
votes

Deleted this question as I decided to perform a major recode and hoped that would fix my problem. It did not, but I figured out the cause.

I looked through box2d and found a couple instances of code similar to this in the polygon shape class:

if (n < 3)
{
    // Polygon is degenerate.
    b2Assert(false);
    SetAsBox(1.0f, 1.0f);
    return;
}

These instances check the number of vertices after various operations and turn the shape into a 2x2 box if there are fewer than 3. One of these operations makes the shape convex. Another checks if vertices are close together (closer than 0.0025f), deleting one if so.

In my case, the problem was simple. Some of my vertices were less than 0.0025f from each other, resulting in them being deleted, the vert count dropping below 3, an assertion being ignored, and then my shape being turned into a 2x2 box. I hope this helps someone out.