morpheus05
morpheus05

Reputation: 4872

How to construct and draw a nice rope with box2d

I created a rope in box2d with RevoluteJoint and a RopeJoint but I have several problems with it:

  1. From time to time, the last segment rotates and it looks like it was disconnected from the rope.
  2. How to draw this thing? I create a texture with the size of each link. If the rope isn't moving it looks good, but as soon as the rope moves and the different links start to rotate slightly, you see gaps between the links.

Code:

private void createChain(World world, Body anchorBody) {
  Body previousBody = anchorBody;

  FixtureDef fixtureDef = new FixtureDef();

  PolygonShape robeLinkShape = new PolygonShape();
  robeLinkShape.setAsBox(4 / PPM, 8 / PPM);
  fixtureDef.shape = robeLinkShape;
  fixtureDef.density = 0.1f;
  //    fixtureDef.friction = 1.0f;
  fixtureDef.restitution = 0.1f;
  fixtureDef.filter.maskBits = Box2DConst.BIT_PLAYER;
  fixtureDef.filter.categoryBits = Box2DConst.BIT_GROUND;

  float mapX = anchorBody.getPosition().x * PPM;
  float mapY = anchorBody.getPosition().y * PPM;

  BodyDef bodyDef = new BodyDef();
  bodyDef.angularDamping = 1.0f;
  bodyDef.linearDamping = 1.0f;

  //create rope
  for (int i = 0; i < 10; i++) {
    Float robeX = mapX / PPM;
    Float robeY = (mapY - (i * 16)) / PPM;
    bodyDef.type = BodyDef.BodyType.DynamicBody;
    bodyDef.position.set(robeX, robeY);

    final Body link = world.createBody(bodyDef);
    link.createFixture(fixtureDef);

    RevoluteJointDef jointDef = new RevoluteJointDef();
    jointDef.initialize(previousBody, link, new Vector2(robeX, robeY));

    //don't need the rope to collide itself
    jointDef.collideConnected = false;
    jointDef.enableLimit = false;

    // because we don't collide with other bodies in the rope, limit rotation to keep the rope bodies from rotating too much.
    jointDef.lowerAngle = -5.0f * MathUtils.degreesToRadians;
    jointDef.upperAngle = 5.0f * MathUtils.degreesToRadians;
    world.createJoint(jointDef);

    links.add(link);
    previousBody = link;
  }

  RopeJointDef ropeJointDef = new RopeJointDef();
  ropeJointDef.localAnchorB.set(0, 0);
  ropeJointDef.maxLength = 90.0f;
  ropeJointDef.bodyB = previousBody;
  ropeJointDef.bodyA = links.get(0);
  ropeJointDef.collideConnected = false;
  world.createJoint(ropeJointDef);
}

public void draw(final SpriteBatch batch) {
  Texture texture = FipiGame.res.get("rope");
  batch.begin();
  for (Body link : links) {
    float x = (link.getPosition().x * PPM) - 4;
    float y = (link.getPosition().y * PPM) - 8;

    float angleDeg = MathUtils.radiansToDegrees * link.getAngle();
    batch.draw(texture, x, y, 0, 0, texture.getWidth(), texture.getHeight(), 1f, 1f, angleDeg, 0,   0,
             texture.getWidth(), texture.getHeight(), false, false);
   }
batch.end();
}

Upvotes: 0

Views: 570

Answers (1)

DylanVann
DylanVann

Reputation: 493

Instead of drawing based on body positions and rotations: Create a set of points by looping through the revolute joint positions (midpoint of anchorA and anchorB in world space). Then draw your sprites so they connect those positions. This will be somewhat inaccurate in that it won't perfectly line up with the positions of the rigid bodies, but it should look all right.

Upvotes: 1

Related Questions