Pergunta

Edit 2: http://youtu.be/KiCzUZ69gpA - as you can see in this video, the shaking effect is amplified when I also render some text for each body. Observe how the ground body (blue) shakes violently when each body has some text rendered near it, and how it does not when text rendering is commented out. This has to be connected!

Edit: I've made two important additions to the original question: I've added my rendering functions, and the camera (translation) methods, and I think that the error is actually there, not in JBox2D.

I'm trying to simulate and render a lot of random bodies (2-20) connected with RevoluteJoints. One body can be connected to multiple others, and there are no separate constructions, i.e. all the bodies are interconnected.

However, when watching the live rendering, it is very shaky and unstable. By that I mean that bodies' positions (or maybe angles) seem to be randomly fluctuating for no apparent reason, making the simulation look unstable.

Here's a video of what I'm observing:

http://youtu.be/xql-ypso1ZU

Notice the middle square and the rotating rectangle. The middle square is shifting its position back and forth slightly at seemingly random intervals, and the rotating rectangle is very jittery (take a look at the point it is rotating about).

What could this effect be due? Is it some known issue with (J)Box2D, or is it an issue with my rendering system? I think that I have somehow misconfigured the physics engine, but also some floating point math in the rendering system could be the culprit.

Here's how I'm creating the bodies and the joints:

private Body setPart(Part part) {
    // body definition
    BodyDef bd = new BodyDef();
    bd.position.set(0f, -10f);
    bd.angle = 0f;
    bd.type = BodyType.DYNAMIC;

    // define shape of the body.
    PolygonShape Shape = new PolygonShape();
    Shape.setAsBox(part.width / 2, part.height / 2);

    // define fixture of the body.
    FixtureDef fd = new FixtureDef();
    Filter filter = new Filter();
    filter.groupIndex = -1;
    fd.filter = filter;
    fd.shape = Shape;
    fd.density = 0.5f;
    fd.friction = 0.3f;
    fd.restitution = 0.5f;

    // create the body and add fixture to it
    Body body = world.createBody(bd);
    body.createFixture(fd);
    body.setUserData(new PartUserData());

    return body;
}

private void setJoint(PartJoint partJoint) {
    Body bodyOne = partToBody.get(partJoint.partOne);
    Body bodyTwo = partToBody.get(partJoint.partTwo);

    RevoluteJointDef jointDef = new RevoluteJointDef();
    jointDef.bodyA = bodyOne;
    jointDef.bodyB = bodyTwo;

    jointDef.localAnchorA = partJoint.partOne
            .getAnchor(partJoint.percentOne);
    jointDef.localAnchorB = partJoint.partTwo
            .getAnchor(partJoint.percentTwo);

    // rotation
    jointDef.lowerAngle = GeomUtil.circle(partJoint.rotateFrom);
    jointDef.upperAngle = GeomUtil.circle(partJoint.rotateTo);
    jointDef.enableLimit = true;
    jointDef.maxMotorTorque = 10.0f; // TODO limit maximum torque
    jointDef.motorSpeed = GeomUtil.circle(partJoint.angularVelocity);
    jointDef.enableMotor = true;

    world.createJoint(jointDef);
}

The time step is 0.01f.

Here is how I draw bodies:

private void drawBody(Body body) {
    // setup the transforms
    Vector position = camera.translate(body.getPosition());
    currentGraphics.translate(position.x, position.y);
    currentGraphics.rotate(body.getAngle());

    // do the actual rendering
    for (Fixture fixture = body.getFixtureList(); fixture != null; fixture = fixture
            .getNext()) {
        PolygonShape shape = (PolygonShape) fixture.getShape();

        if (body.getUserData() instanceof PartUserData) {
            fillShape(shape, partFillColor);
            currentGraphics.setStroke(partOutlineStroke);
            outlineShape(shape, partOutlineColor);
        } else {
            fillShape(shape, groundFillColor);
            outlineShape(shape, groundOutlineColor);
        }
    }

    // clean up
    currentGraphics.rotate(-body.getAngle());
    currentGraphics.translate(-position.x, -position.y);
    currentGraphics.setColor(defaultColor);
    currentGraphics.setStroke(defaultStroke);
}

I think that the issue might be the way I'm handling rendering of all the bodies.

This is the algorithm for each body: 1. Translate the Graphics2D object to its position 2. Rotate it by body.getAngle() 3. Render the body 4. Rotate the graphics back 5. Translate the graphics back

Could it be that amongst all these transforms something goes wrong?

When I removed the calls to camera's methods, the effect seems to have been reduced. These are the relevant camera methods:

public Vector translate(Vec2 worldPosition) {
    Vector point = new Vector();

    point.x = (int) (worldPosition.x * pixelsPerMeter) - position.x;
    point.y = (int) (worldPosition.y * pixelsPerMeter) - position.y;

    point.x = (int) (point.x * zoom);
    point.y = (int) (point.y * zoom);

    point.x += renderer.getWidth() / 2;
    point.y += renderer.getHeight() / 2;

    return point;
}

public Vector translateRelative(Vec2 worldPosition) {
    Vector point = new Vector();

    point.x = (int) (worldPosition.x * pixelsPerMeter);
    point.y = (int) (worldPosition.y * pixelsPerMeter);

    point.x = (int) (point.x * zoom);
    point.y = (int) (point.y * zoom);

    return point;
}

But what part of them would cause an issue?

Foi útil?

Solução

tl;dr: I've found a solution, but haven't identified the exact problem. Quite sure it's with my translation methods.

It seems that I have identified the scope of the problem and the solution, but I am still not sure what exactly is causing this behavior.

In those translation formulas I posted in the question, all JBox2D vectors are multiplied by a scale called pixelsPerMeter. When I set this scale to a low value, the shaking effect occurs (it's also important to note that there is another factor as well, called zoom, which is usually greater for a lower pixelsPerMeter).

So, it could be that when multiplying by a relatively low pixelsPerMeter, I have to multiply with a higher zoom factor, and since I'm converting to ints in both steps, there could be some errors in the floating point math or something. Please see the translation methods I've posted in the question.

Here's a video that demonstrates this: (to be uploaded)

Notice that when I set the pixelsPerMeter to 250, shaking seems to be gone, while when I set it to 25, it's quite visible.

Outras dicas

Your solution was the correct one. You are not supposed to use pixel units for the Box2D physics engine :)

http://box2d.org/2011/12/pixels/

and

https://code.google.com/p/box2d/wiki/FAQ#How_do_I_convert_pixels_to_meters?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top