Skip to content
Lesson 9 of 14

Step 1 of 5 · Reading · ~4 min

Read

Collision Response

Constraints (Joints)

So far bodies move freely. A rope, a chain, a hinged door or a ragdoll limb is a body that is not free: something ties it to another body or to the world. After this lesson your program will hold particles together at a fixed distance by repeatedly projecting them back onto the constraint, and will compute the force of a damped spring between two points.

Two ways to connect bodies

  • A spring applies a force that grows with the stretch (Hooke's law). It is easy, but it is soft: the link always stretches a little under load, and a stiff spring needs a tiny time step to stay stable.
  • A constraint says "this distance must equal that length" and the solver moves the bodies until it does. It is rigid, and stable at any stiffness, but it needs a solver loop.

Both appear in real engines. Box2D's distance joint can be either, depending on a frequency setting.

The distance constraint

Two particles A and B must stay L apart. Let d = pB - pA and dist = |d|. The error is dist - L: positive when stretched, negative when compressed. To fix it, move the particles along d, A toward B and B toward A, so the distance shrinks by the full error. How the correction is split between them depends on inverse mass w (0 for a pinned particle, which never moves):

k  = (dist - L) / dist / (wA + wB)
pA = pA + d * k * wA
pB = pB - d * k * wB

The lighter particle (larger w) moves more. If both are pinned (wA + wB = 0) there is nothing to move. If the two coincide, dist = 0, the direction is undefined and the constraint is skipped.

Worked trace: A at (0,0), B at (0,5), L = 3, both with w = 1. d = (0,5), dist = 5, k = 2/5/2 = 0.2. A moves to (0,1), B to (0,4): the distance is now 3. If A is pinned (w = 0) then k = 2/5/1 = 0.4, and B moves by (0,2) alone to (0,3).

Many constraints: iterate

Fixing one constraint can break its neighbour. Take a chain: P0 pinned at (0,0), P1 at (2,0), P2 at (4,0), two rods of length 1. One pass over the rods in order:

rod 0-1:  P1 -> (1,0)                 P0-P1 is fixed
rod 1-2:  P1 -> (2,0)  P2 -> (3,0)    P1-P2 is fixed, but P0-P1 is broken again

A second pass leaves P1 at (1.5, 0) and P2 at (2.5, 0), and each further pass shrinks the error again. This is Gauss-Seidel iteration: visit the constraints one after the other, each time using the positions the previous one already changed. Two consequences matter:

  • The answer after a fixed number of passes depends on the order of the constraints, and it converges as the number of passes grows. Long chains need many passes; that is why a stiff rope of 100 links sags in engines with few iterations.
  • More iterations cost time linearly, so engines pick a budget (Box2D: about 8 velocity and 3 position iterations).

Ropes: one-sided constraints

A rope resists being stretched but goes slack when its ends come closer. It is the same projection, applied only when dist > L. A slack rope does nothing.

Springs

For a spring of stiffness k, rest length L, and damping c, let u = d / dist be the unit vector from A to B and vrel = (vB - vA) . u the speed at which the ends separate. The force on A along u is:

F = k * (dist - L) + c * vrel

The first term pulls A toward B when stretched (and pushes away when compressed). The second term resists the motion itself; without it a spring oscillates forever, and in practice the oscillation grows. B gets the opposite force. For dist = 0 return the zero force.

Common mistakes

  • Splitting the correction 50/50 regardless of mass. A pinned particle would move.
  • Dividing by dist when it is zero (two coincident particles) and spreading nan through the chain.
  • Applying a rope's correction when it is slack, which makes it behave like a rod.
  • Solving each constraint once and expecting a chain to be rigid.
  • Updating copies of the positions, so later constraints do not see the earlier corrections.

Your exercise

The starter reads the scene, reports the residual error after solving, and prints the positions. You write project_pass, one Gauss-Seidel pass over every constraint in input order, and spring_force. The most likely mistake is forgetting that a pinned particle must not move.

Up nextCoulomb FrictionCollision Response

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…