O que faz com que as juntas de revolução do Box2D se separem?

7

Criei uma boneca de pano usando corpos dinâmicos (retângulos) e juntas de revolução simples (com ângulos inferiores e superiores). Quando minha boneca de pano atinge o chão (que é um corpo estático), os corpos parecem se mexer e as articulações se separam.

Parece que os corpos estão grudados no chão e o momento da boneca de pano separa a junta (veja a captura de tela abaixo).

texto alternativo

Não tenho certeza se está relacionado, mas estou usando o wrapper Java Badlogic GDX para Box2D. Aqui estão alguns trechos do que eu acho que é o código mais relevante:

private RevoluteJoint joinBodyParts(
    Body a, Body b, Vector2 anchor, 
    float lowerAngle, float upperAngle) {

    RevoluteJointDef jointDef = new RevoluteJointDef();

    jointDef.initialize(a, b, a.getWorldPoint(anchor));

    jointDef.enableLimit = true;
    jointDef.lowerAngle = lowerAngle;
    jointDef.upperAngle = upperAngle;

    return (RevoluteJoint)world.createJoint(jointDef);
}

private Body createRectangleBodyPart(
    float x, float y, float width, float height) {

    PolygonShape shape = new PolygonShape();
    shape.setAsBox(width, height);

    BodyDef bodyDef = new BodyDef();
    bodyDef.type = BodyType.DynamicBody;
    bodyDef.position.y = y;
    bodyDef.position.x = x;

    Body body = world.createBody(bodyDef);

    FixtureDef fixtureDef = new FixtureDef();
    fixtureDef.shape = shape;
    fixtureDef.density = 10;

    fixtureDef.filter.groupIndex = -1;
    fixtureDef.filter.categoryBits = FILTER_BOY;
    fixtureDef.filter.maskBits = FILTER_STUFF | FILTER_WALL;

    body.createFixture(fixtureDef);
    shape.dispose();

    return body;
}

Eu pulei o método para criar a cabeça, pois é praticamente o mesmo que o método retângulo (apenas usando uma forma de cricle).

Esses métodos são usados ​​assim:

    torso = createRectangleBodyPart(x, y + 5, 0.25f, 1.5f);
    Body head = createRoundBodyPart(x, y + 7.4f, 1);

    Body leftLegTop = createRectangleBodyPart(x, y + 2.7f, 0.25f, 1);
    Body rightLegTop = createRectangleBodyPart(x, y + 2.7f, 0.25f, 1);
    Body leftLegBottom = createRectangleBodyPart(x, y + 1, 0.25f, 1);
    Body rightLegBottom = createRectangleBodyPart(x, y + 1, 0.25f, 1);

    Body leftArm = createRectangleBodyPart(x, y + 5, 0.25f, 1.2f);
    Body rightArm = createRectangleBodyPart(x, y + 5, 0.25f, 1.2f);

    joinBodyParts(torso, head, new Vector2(0, 1.6f), headAngle);

    leftLegTopJoint = joinBodyParts(torso, leftLegTop, new Vector2(0, -1.2f), 0.1f, legAngle);
    rightLegTopJoint = joinBodyParts(torso, rightLegTop, new Vector2(0, -1.2f), 0.1f, legAngle);
    leftLegBottomJoint = joinBodyParts(leftLegTop, leftLegBottom, new Vector2(0, -1), -legAngle * 1.5f, 0);
    rightLegBottomJoint = joinBodyParts(rightLegTop, rightLegBottom, new Vector2(0, -1), -legAngle * 1.5f, 0);

    leftArmJoint = joinBodyParts(torso, leftArm, new Vector2(0, 1), -armAngle * 0.7f, armAngle);
    rightArmJoint = joinBodyParts(torso, rightArm, new Vector2(0, 1), -armAngle * 0.7f, armAngle);
Nick Bolton
fonte
Você executa sua simulação física com um passo fixo no tempo? Caso contrário, você definitivamente deve fazer isso primeiro.
bummzack

Respostas:

5

O Box2D usa um solucionador de forma iterativo , em vez de fechado . Isso significa que todas as restrições são um pouco suaves. O Box2D se esforçará muito para fazer o que você diz, mas ocorrem imprecisões.

Para minimizar isso, há várias coisas que você pode fazer, em ordem de importância:

  • Use timesteps de tamanho fixo (o primeiro argumento para Step). Isso torna o solucionador mais estável - é mais provável que suas imprecisões se cancelem ou se comportem pelo mesmo sempre.
  • Use um timestep menor (1/60 ou 1/120 em vez de 1/30). Timesteps menores significam imprecisões menores.
  • Use mais iterações do solucionador (o segundo / terceiro argumento para Step). Você deve tentar obter pelo menos 10 de cada. Mais iterações de solucionador significam mais chances a cada passo de encontrar estabilidade e satisfazer corretamente as restrições - aproximadamente, menos chance de imprecisão. No entanto, um timestep menor e mais estável é melhor que mais iterações. Dadas as restrições suficientemente complicadas, você nunca poderá resolvê-las 100% corretamente, portanto, tente reduzir o tempo máximo antes de aumentar as iterações.
  • Use formas menos densas. A densidade 10 é bastante pesada. Tente padronizar algo menor - como 1 - como sua densidade padrão.

fonte
Oi Joe, obrigado pelo conselho. Eu tentei todas as suas sugestões, mas infelizmente isso não pareceu fazer a diferença. Aqui está o código-fonte completo (é apenas um protótipo hacky no momento) - pastebin.com/F6YX3AyV
Nick Bolton