androidlibgdxbox2drevolute-joints

LibGDX Box2D rope stretching like a crazy


I am developing a game when i using a rope, that i created in Box2d using circles and joints. Rope works as expected when i drag it with low force, and in other cases (when force is more big) it just stretch like a crazy and i don't know how to fix it and create more stable rope. I tried diferent types of joints( RopeJoint, RevoluteJoint, DistanceJoint, etc.), but it's useless. Here is my code:

public class RopeItem {

public Body body;
private com.badlogic.gdx.graphics.g2d.Sprite bodySprite;
float width, length;
public CircleShape shape;
public RopeItem(World world, float width, float height, BodyType bodyType, Vector2 position) {
    super();        

    //init body 
    BodyDef bodyDef = new BodyDef();
    bodyDef.type = bodyType;
    bodyDef.position.set(position);
    this.body = world.createBody(bodyDef);
    //init shape
    FixtureDef fixtureDef = new FixtureDef();
    fixtureDef.density = 0.1f;
    fixtureDef.friction = 0.4f; 
    fixtureDef.restitution  = 0.2f;
    shape = new CircleShape();
    shape.setRadius(width / 2);
    fixtureDef.shape = shape;
    fixtureDef.filter.categoryBits = 0x0001;
    fixtureDef.filter.maskBits = 0x0002;

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

    //create a body sprite
    bodySprite = new com.badlogic.gdx.graphics.g2d.Sprite(new TextureRegion(new Texture("data/rope_circle.png")));
    bodySprite.setSize(width, height);
    bodySprite.setOrigin(width/2, height/2);
    body.setUserData(bodySprite);

}

}

public void createRope(int length) {
    RevoluteJoint[] joints = new RevoluteJoint[length - 1];
    RopeJoint[] ropeJoints = new RopeJoint[length - 1];
    float width = 0.23f, height = 0.23f;
    for(int i=0; i<length; i++) {
        ropeSegmenets.add(new RopeItem(world, width, height, BodyType.DynamicBody, new Vector2(0.3f, 0)));
    }

    RevoluteJointDef jointDef = new RevoluteJointDef();
    jointDef.localAnchorA.x = -height / 2;
    jointDef.localAnchorB.x = height / 2;

    RopeJointDef ropeJointDef = new RopeJointDef();
    ropeJointDef.localAnchorA.set(0, -height / 2);
    ropeJointDef.localAnchorB.set(0, height / 2);
    ropeJointDef.maxLength = length;    

    for(int i = 0; i < length-1; i++) {
        jointDef.bodyA = ropeSegmenets.get(i).body;
        jointDef.bodyB = ropeSegmenets.get(i + 1).body;
        joints[i] = (RevoluteJoint) world.createJoint(jointDef);

        ropeJointDef.bodyA = ropeSegmenets.get(i).body;
        ropeJointDef.bodyB = ropeSegmenets.get(i+1).body;
        ropeJoints[i] = (RopeJoint) world.createJoint(ropeJointDef);
    }
}

Screenshots:

Normal:

enter image description here

Stretched:

enter image description here

If you know how to fix this problem, please, help me.

Thanks, Charlie!


Solution

  • It happens because each piece of the chain only solves its position constraints locally, that is, it only looks at its two closest neighbors. It pulls those neighbors toward itself, and also moves itself toward their center. A piece at the middle of the chain does not care about the fixed point that you attached the end of the rope to.

    The only guaranteed way to fix it completely is to have every piece of the chain connected to every other piece with a rope joint. So if you have n bodies, you will need (n^2+n)/2 joints.

    The next best thing would be to have every piece of the chain connected to the fixed end with a rope joint. That would be 2n joints. However, if you want to pull the rope around, then you would also need to have a rope joint to connect every piece of the chain that the player can pick and drag. If you want to let the player pick and drag wherever they like, you're back to the (n^2+n)/2 case again.

    You might be able to do that partially, eg. spread some extra rope joints evenly along the line, to connect just some points to the end of the chain.

    If your rope does not need to have a physically collidable presence along the full length, then I would suggest using rope joints to connect the two ends and just render a spline or something in between using verlet rope points.

    However from the screenshot, it looks like you are trying to hang the rope on collidable points and pull it around them with tension. I really don't think you will enjoy the experience of trying to do this with Box2D. What you need for that is a custom-made behavior specifically designed for rope physics. I don't know of any nice existing source code out there, but you could get some good ideas from this video: https://www.youtube.com/watch?v=Krlf1XnzZGc