Game Physics JS: Springs

Hook's law

The force applied by a spring to both of its ends, when it is extended or compressed, is:

f = -k(|d| - l0)d̂

With a spring stiffness k, rest length l0, length |d| and direction .

The force's direction points to the end of the spring that interests us (the force at the other end will be equal to -f).

A spring's limit of elasticity (min / max length) can be enforced in code, but it's rarely useful.

Spring-like force generators

1) Basic spring

Applies a spring force to a particle.

If there's a movable particle at both ends of the spring, call updateForce for both.

If the spring is used to make the camera follow a character, use it only for the camera (the character must not be affected by the camera's spring).

class ParticleSpring { other; springConstant; restLength; constructor(other, springConstant, restLength){ this.other = other; this.springConstant = springConstant; this.restLength = restLength; } updateForce(particle){ // Calculate the vector of the spring var force = new Vector3(); force.sub(this.other.position); // Calculate the magnitude of the force var magnitude = force.magnitude(); magnitude = Math.abs(magnitude - this.restLength) * this.springConstant; // Calculate the final force and apply it force.normalize(); force.scale(-magnitude); particle.addForce(force); } }

2) Anchored spring

Same thing but instead of an "other" particle, there's an "anchor" point not affected by the spring.

3) Elastic bungee

Contrary to a complete spring, the bungee only has a pulling force when extended.

// Check if the bungee is compressed. var magnitude = force.magnitude(); if(magnitude <= this.restLength) return;

4) Buoyancy

Springs can be used to approach buoyancy forces.

The buoyancy force is 0 when the object is out of water, and fixed when it is fully submerged.

f = dvρ

Where d is the object's submersion level (0 <= d <= 1), v is the object's volume and ρ the liquid's density.

If the object is partially submerged:

d = (object_height - liquid_plane - submersion_depth) / 2 * (submersion_depth)

class ParticleBuoyancy { maxDepth; volume; waterHeight; liquidDensity; constructor(maxDepth, volume, waterHeight, liquidDensity = 1000){ this.maxDepth = maxDepth; this.volume = volume; this.waterHeight = waterHeight; this.liquidDensity = liquidDensity; } updateForce(particle){ // Calculate the submersion depth var depth = particle.position.y; // Check if we’re out of the water if (depth >= this.waterHeight + this.maxDepth) return; var force = Vector3(0,0,0); // Check if we’re at maximum depth if (depth <= waterHeight - maxDepth){ force.y = this.liquidDensity * this.volume; particle.addForce(force); return; } // Otherwise we are partly submerged force.y = this.liquidDensity * this.volume * (this.depth - this.maxDepth - thi.waterHeight) / 2 * this.maxDepth; particle.addForce(force); } }

Demo

Stiff springs

Stiff springs are used in many games to resolve collisions (objects interpenetrating), but are hard to fine tune.



Next