1
\$\begingroup\$

Wasn't sure whether to post here or on SO so please move if needed.

I've been having a look into the box2D physics API provided by LOVE to try and create a swinging flail or weighted rope but I'm having some trouble keeping the jointed elements close together.

So far I've made a series of circular bodies and linked them together using revolute joints with the first element being static and the final element being slightly larger/heavier to act as a weight.

The code to generate each link is as follows:

    for i = 1, segments, 1 do

        link = {}

        if (i == 1) then
            link.body = love.physics.newBody(world, xpos, ypos, "static") --Starting link
        else
            link.body = love.physics.newBody(world, xpos, ypos, "dynamic")
        end

        if (i == segments) then
            link.shape = love.physics.newCircleShape(endlink_radius) --Ending link
        else
            link.shape = love.physics.newCircleShape(link_radius) 
        end

        link.fixture = love.physics.newFixture(link.body, link.shape) --Fix bodies to shapes

        table.insert(chain,link) 

        ypos = ypos + link_distance --Place next link further down, keeping x position

    end

And the code to join each link:

    for i = 2, #chain, 1 do

        x1,y1 = chain[i-1].body:getPosition() --First link position
        x2,y2 = chain[i].body:getPosition() --Second link position
        chain[i-1].join = love.physics.newRevoluteJoint(chain[i-1].body, chain[i].body, x1, y1, x2, y2, false ) --Join the two with a revolute joint

    end

And this works quite well, producing a chain like this:

Chain1

The next step is to move the chain, I'm currently moving the first static element and having it "drag" the rest of the links along with it using the following code:

    position = vo.add(position, velocity) --Vector addition add velocity to current position to get new position
    chain[1].body:setPosition(position.x, position.y) --Update the first link position

This works fine for slow speeds (chain currently moves towards mouse cursor) but any rapid movement causes everything to fall apart.

Chain2

Is there anyway to easily keep the elements close together?

I've tried adjusting the weights and world gravity but couldn't see any difference, I've also thought about applying a force to each element when the first one moves but that doesn't seem quite right.

Thanks

Solution (Thanks to NauticalMile)

Change the previous position update:

    position = vo.add(position, velocity) --Vector addition add velocity to current position to get new position
    chain[1].body:setPosition(position.x, position.y) --Update the first link position

To a velocity update and have Box2D handle the movement:

    chain[1].body:setLinearVelocity(velocity.x, velocity.y)
\$\endgroup\$
3
\$\begingroup\$

Directly editing the position of a box2d body will produce non-physical behaviour, it's present because sometimes you will need to teleport bodies to a far away location, or reset the position of an object without the need to destroy/re-create it, etc... In your case the joints are not handling the position adjustments well.

The setPosition function should not be used to advance the physics, box2d takes care of that all on its own when b2World:step is called. In order for the chain to move smoothly, you just need to manipulated the top link in a different manner. I propose one of two suggestions:

  1. If the top link in the chain is supposed to be attached to something much bigger moving at a constant velocity, then I would recommend changing the bodyType of the top link to 'kinematic' using the Body:setType function. Just initialize the velocity of the top link body to a reasonable value. This is demonstrated in RUBE:

kinematic_body

  1. If you want the top link to have a dynamic behaviour, while still constraining it to only move along a given axis, you can create a 'dummy' static body, and connect the top link to the static body via a PrismaticJoint. Again, here's an example done in RUBE where I have applied a small force at the beginning using the MouseJoint:

prismatic_joint

In the real world objects accelerate when forces are applied to them. box2d functions in much the same way; enter the body:applyForce function. You can use this to apply a force toward the cursor position. You can also add some LinearDamping to the first or any of the links to make sure things don't get too out of hand.

Both of these options should be very stable, but let me know if there are any issues.

\$\endgroup\$
  • \$\begingroup\$ Thank you, your first solution is working well so far. Simply setting a linear velocity on the first element was all it took. \$\endgroup\$ – user3510227 Jun 30 '15 at 10:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.