[libgdx] Fixed physics jitter at low update and rendering rates.

https://esotericsoftware.com/forum/d/26512-physics-jittersunstable-at-higher-framerates/16
This commit is contained in:
Nathan Sweet 2025-04-19 13:11:45 -04:00
parent 67f09f79ed
commit f05a1524cd

View File

@ -39,10 +39,10 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
boolean reset = true; boolean reset = true;
float ux, uy, cx, cy, tx, ty; float ux, uy, cx, cy, tx, ty;
float xOffset, xVelocity; float xOffset, xLag, xVelocity;
float yOffset, yVelocity; float yOffset, yLag, yVelocity;
float rotateOffset, rotateVelocity; float rotateOffset, rotateLag, rotateVelocity;
float scaleOffset, scaleVelocity; float scaleOffset, scaleLag, scaleVelocity;
float remaining, lastTime; float remaining, lastTime;
public PhysicsConstraint (PhysicsConstraintData data, Skeleton skeleton) { public PhysicsConstraint (PhysicsConstraintData data, Skeleton skeleton) {
@ -63,12 +63,16 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
lastTime = skeleton.time; lastTime = skeleton.time;
reset = true; reset = true;
xOffset = 0; xOffset = 0;
xLag = 0;
xVelocity = 0; xVelocity = 0;
yOffset = 0; yOffset = 0;
yLag = 0;
yVelocity = 0; yVelocity = 0;
rotateOffset = 0; rotateOffset = 0;
rotateLag = 0;
rotateVelocity = 0; rotateVelocity = 0;
scaleOffset = 0; scaleOffset = 0;
scaleLag = 0;
scaleVelocity = 0; scaleVelocity = 0;
} }
@ -97,7 +101,7 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
boolean x = data.x > 0, y = data.y > 0, rotateOrShearX = data.rotate > 0 || data.shearX > 0, scaleX = data.scaleX > 0; boolean x = data.x > 0, y = data.y > 0, rotateOrShearX = data.rotate > 0 || data.shearX > 0, scaleX = data.scaleX > 0;
BonePose bone = this.bone; BonePose bone = this.bone;
float l = bone.bone.data.length; float l = bone.bone.data.length, t = data.step, z = 0;
switch (physics) { switch (physics) {
case none: case none:
@ -116,8 +120,8 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
ux = bx; ux = bx;
uy = by; uy = by;
} else { } else {
float a = remaining, i = pose.inertia, t = data.step, f = skeleton.data.referenceScale, d = -1; float a = remaining, i = pose.inertia, f = skeleton.data.referenceScale, d = -1, qx = data.limit * delta,
float qx = data.limit * delta, qy = qx * Math.abs(skeleton.scaleY); qy = qx * Math.abs(skeleton.scaleY);
qx *= Math.abs(skeleton.scaleX); qx *= Math.abs(skeleton.scaleX);
if (x || y) { if (x || y) {
if (x) { if (x) {
@ -133,7 +137,7 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
if (a >= t) { if (a >= t) {
d = (float)Math.pow(pose.damping, 60 * t); d = (float)Math.pow(pose.damping, 60 * t);
float m = pose.massInverse * t, e = pose.strength, w = pose.wind * f * skeleton.scaleX, float m = pose.massInverse * t, e = pose.strength, w = pose.wind * f * skeleton.scaleX,
g = pose.gravity * f * skeleton.scaleY; g = pose.gravity * f * skeleton.scaleY, xs = xOffset, ys = yOffset;
do { do {
if (x) { if (x) {
xVelocity += (w - xOffset * e) * m; xVelocity += (w - xOffset * e) * m;
@ -147,13 +151,16 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
} }
a -= t; a -= t;
} while (a >= t); } while (a >= t);
xLag = xOffset - xs;
yLag = yOffset - ys;
} }
if (x) bone.worldX += xOffset * mix * data.x; z = Math.max(0, 1 - a / t);
if (y) bone.worldY += yOffset * mix * data.y; if (x) bone.worldX += (xOffset - xLag * z) * mix * data.x;
} if (y) bone.worldY += (yOffset - yLag * z) * mix * data.y;
} else
z = Math.max(0, 1 - a / t);
if (rotateOrShearX || scaleX) { if (rotateOrShearX || scaleX) {
float ca = atan2(bone.c, bone.a), c, s, mr = 0; float ca = atan2(bone.c, bone.a), c, s, mr = 0, dx = cx - bone.worldX, dy = cy - bone.worldY;
float dx = cx - bone.worldX, dy = cy - bone.worldY;
if (dx > qx) if (dx > qx)
dx = qx; dx = qx;
else if (dx < -qx) // else if (dx < -qx) //
@ -162,11 +169,12 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
dy = qy; dy = qy;
else if (dy < -qy) // else if (dy < -qy) //
dy = -qy; dy = -qy;
a = remaining;
if (rotateOrShearX) { if (rotateOrShearX) {
mr = (data.rotate + data.shearX) * mix; mr = (data.rotate + data.shearX) * mix;
float r = atan2(dy + ty, dx + tx) - ca - rotateOffset * mr; float rz = rotateLag * Math.max(0, 1 - a / t), r = atan2(dy + ty, dx + tx) - ca - (rotateOffset - rz) * mr;
rotateOffset += (r - (float)Math.ceil(r * invPI2 - 0.5f) * PI2) * i; rotateOffset += (r - (float)Math.ceil(r * invPI2 - 0.5f) * PI2) * i;
r = rotateOffset * mr + ca; r = (rotateOffset - rz) * mr + ca;
c = cos(r); c = cos(r);
s = sin(r); s = sin(r);
if (scaleX) { if (scaleX) {
@ -179,10 +187,10 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
float r = l * bone.getWorldScaleX(); float r = l * bone.getWorldScaleX();
if (r > 0) scaleOffset += (dx * c + dy * s) * i / r; if (r > 0) scaleOffset += (dx * c + dy * s) * i / r;
} }
a = remaining;
if (a >= t) { if (a >= t) {
if (d == -1) d = (float)Math.pow(pose.damping, 60 * t); if (d == -1) d = (float)Math.pow(pose.damping, 60 * t);
float m = pose.massInverse * t, e = pose.strength, w = pose.wind, g = pose.gravity, h = l / f; float m = pose.massInverse * t, e = pose.strength, w = pose.wind, g = pose.gravity, h = l / f,
rs = rotateOffset, ss = scaleOffset;
while (true) { while (true) {
a -= t; a -= t;
if (scaleX) { if (scaleX) {
@ -201,6 +209,8 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
} else if (a < t) // } else if (a < t) //
break; break;
} }
rotateLag = rotateOffset - rs;
scaleLag = scaleOffset - ss;
} }
} }
remaining = a; remaining = a;
@ -209,12 +219,13 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
cy = bone.worldY; cy = bone.worldY;
break; break;
case pose: case pose:
if (x) bone.worldX += xOffset * mix * data.x; z = Math.max(0, 1 - remaining / t);
if (y) bone.worldY += yOffset * mix * data.y; if (x) bone.worldX += (xOffset - xLag * z) * mix * data.x;
if (y) bone.worldY += (yOffset - yLag * z) * mix * data.y;
} }
if (rotateOrShearX) { if (rotateOrShearX) {
float o = rotateOffset * mix, s, c, a; float o = (rotateOffset - rotateLag * z) * mix, s, c, a;
if (data.shearX > 0) { if (data.shearX > 0) {
float r = 0; float r = 0;
if (data.rotate > 0) { if (data.rotate > 0) {
@ -244,7 +255,7 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
} }
} }
if (scaleX) { if (scaleX) {
float s = 1 + scaleOffset * mix * data.scaleX; float s = 1 + (scaleOffset - scaleLag * z) * mix * data.scaleX;
bone.a *= s; bone.a *= s;
bone.c *= s; bone.c *= s;
} }