I had the same issue, personally found using a physics substep algorithm did the trick to stabilize more at lower fps:
Got the idea from also a unity tutorial on aircraft haha:
--Calculate force normal, used in vector forces
local upwardForce = calculateForces(targetHeight, currentHeight, currentYVelocity, t, mass, hipHeight, dt)
local function physicsSubstep(substepUpwardForce, step, substepYVelocity, iterateHeight)
local netForce = substepUpwardForce - weight
local predictedVelocity = predictVelocity(netForce, mass, substepYVelocity, step)
local predictedDisplacement = predictDisplacement(netForce, mass, substepYVelocity, step)
local newUpwardForce = calculateForces(targetHeight, iterateHeight+predictedDisplacement, predictedVelocity, t, mass, hipHeight, dt)
local averageVelocity = (substepYVelocity+predictedVelocity) * 0.5
local averageForce = (newUpwardForce+ substepUpwardForce)*0.5
local averageHeight = iterateHeight+predictedDisplacement*0.5
return averageForce, averageVelocity, averageHeight
end
--Perform physics substep
local iterateForce = upwardForce
local iterateYVelocity = currentYVelocity
local iterateHeight = currentHeight
local n = 3 --Split a frame into multipe "Substep" frames
local step = dt/n
for _ = 1, n-1 do
iterateForce, iterateYVelocity, iterateHeight = physicsSubstep(iterateForce, step, iterateYVelocity, iterateHeight)
end
upwardForce = iterateForce
Scenario for stiff spring suspension on a custom character:
Before:
After: