I'm trying to simulate a chain with pymunk and pygame.
To do this i have the objects Chain, ChainLink, and ChainEstremity. They are respectively: the container for all the chainlinks, a capsule shaped object which consists in two pymunk Circle objects, and a static box where the initial or final chain links are attached to.
Chainlinks have 4 costraints, 3 PinJoints to keep their shape intact (remember they consist in 2 shapes), and 1 RotaryLimitJoint to avoid a rotation bigger than 90 degrees. Multiple chain links can be linked together with the link_to function which adds a SlideJoint. And finally a chain link and a chain estremity are attached with a PinJoint. My code can be found on github here.
My problem is that the chain keeps shaking randomly and doesn't stop with time, making the simulation unrealistic. I tried adjusting the mass of the bodies and adding a manual damping to the angular velocities of the bodies but none of these measures have any effect at all.
Here's what the chain looks like:

Here is how i init the classes and the link_to function:
class ChainEstremity:
def __init__(self, center):
half_side = ChainEstremity.half_side
self.body = pymunk.Body(body_type=pymunk.Body.STATIC)
self.shape = pymunk.Poly(self.body,
[(-half_side,-half_side), (half_side,-half_side), (half_side,half_side), (-half_side, half_side)]
)
self.shape.mass = ChainEstremity.mass
self.body.position = center
space.add(self.body, self.shape)
class Chain:
def __init__(self, point_a, point_b, chain_links):
Chain.estremities.append(ChainEstremity(point_a))
Chain.estremities.append(ChainEstremity(point_b))
estremities_dist = Chain.estremities[0].body.position.get_distance(Chain.estremities[1].body.position)
links_length = estremities_dist // chain_links
first_link = ChainLink(links_length)
Chain.all_links.append(first_link)
first_link.setup(Chain.estremities[0].body.position)
first_link_constraint = pymunk.constraints.PinJoint(Chain.estremities[0].body, first_link.shape1.body)
first_link_constraint.collide_bodies = False
space.add(first_link_constraint)
for i in range(1, chain_links):
new_link = ChainLink(links_length)
Chain.all_links.append(new_link)
new_link.setup(Chain.estremities[0].body.position + Vec2d(links_length * i, 0))
new_link.link_to(Chain.all_links[i-1])
if i == chain_links - 1:
last_link_costraint = pymunk.constraints.PinJoint(Chain.estremities[1].body, new_link.shape2.body)
last_link_costraint.collide_bodies = False
space.add(last_link_costraint)
class ChainLink:
def __init__(self, length):
self.length = length
self.shape1 = pymunk.Circle(pymunk.Body(), ChainLink.radius)
self.shape2 = pymunk.Circle(pymunk.Body(), ChainLink.radius)
self.shape1.mass = ChainLink.mass
self.shape2.mass = ChainLink.mass
self.rotary_limit = pymunk.constraints.RotaryLimitJoint(self.shape1.body, self.shape2.body, 0, 0)
space.add(self.shape1, self.shape1.body, self.shape2, self.shape2.body, self.rotary_limit)
def link_to(self, other):
#NOTE: in the chain this object links to the previous one added
slide_constraint = pymunk.constraints.SlideJoint(self.shape1.body, other.shape2.body,
Vec2d(0, 0), Vec2d(-ChainLink.radius, 0),
other.get_full_length() // 2, (other.get_full_length() + ChainLink.radius) // 2)
rotary_constraint = pymunk.constraints.RotaryLimitJoint(self.shape1.body, other.shape2.body, -math.pi // 2, math.pi // 2)
slide_constraint.collide_bodies = False
rotary_constraint.collide_bodies = False
space.add(slide_constraint, rotary_constraint)
I have a couple of suggestions you can try:
Make the
ChainLinka single object. You can to that by attaching the two circle shapes to the same body with an offset. This way you simplify the setup quite a bit since you wont need constraints to keep it together.You can experiment with different values for
max_biasand/ormax_forceon the constraints.You can try setting
space.dampingto a value slightly lower than 1.