Wednesday, February 17, 2016

LOVE Physics - Breaking Joint Chains (LUA / Box2D)


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)

Answer



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.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...