Spring float value

Hi All,

Every now and then I’ve wanted to be able to add a springyness to a float but I’ve never found a nice way to do it. I’d like to have a ‘target’ float that the springy one aims to reach like the blue line in this pic.

I don’t really want to use a tweener script as they tend to be locked to a preset (springy) curve, I’d like something more dynamic.
Any ideas where I could start with that?

Thanks,
Pete

You can use Unity’s built-in AnimationCurve class directly for that; in particular, the Evaluate() function. Doesn’t matter if you actually end up using the value for an animation, the class is more generically useful than its name would imply.

And if you make it a public field on a MonoBehaviour, it’ll show up in the inspector and let you edit the curve visually, so you can tweak it to get the exact springiness you’re looking for.

Thanks Andy,
I was looking for something that could work more dynamically than the animation curve (or a tween system).
I’m trying to get my value to ‘chase’ the target vale and overshoot dynamically as the target changes.

Ah, that kind of more dynamic, I see. You might have success by simply implementing the math of an ideal spring with some damping; it’s not too complicated. You’ll need to track the current position of the float value and its current velocity. Then during each (fixed) frame, you reduce the current velocity by multiplying it by the damping factor (something a bit less than 1). You further alter the velocity further by adding a force proportional to the current value subtracted from the target value (stronger force if the current value is further away from the target value, weaker if it’s close). Update the current value based on this new velocity.

You can of course use higher quality integration techniques to keep the physics clean, but the simplest form might be good enough to get a passable result, especially if you take steps to detect that the velocity is sufficiently small and current value sufficiently close to the target value such that you simply set velocity to zero and the current value to the target value.

Here’s an untested shot at some sample code. Mass has been removed from the equations, since you’re not necessarily dealing with literally physical systems, and stiffness as a tweakable parameter should be enough to get the same effect anyway.

float currentValue;
float currentVelocity;
float targetValue;
float stiffness = 1f; // value highly dependent on use case
float damping = 0.1f; // 0 is no damping, 1 is a lot, I think
float valueThreshold = 0.01f;
float velocityThreshold = 0.01f;

void FixedUpdate()
{
    float dampingFactor = Mathf.Max(0, 1 - damping * Time.fixedDeltaTime);
    float acceleration = (targetValue - currentValue) * stiffness * Time.fixedDeltaTime;
    currentVelocity = currentVelocity * dampingFactor + acceleration;
    currentValue += currentVelocity * Time.fixedDeltaTime;

    if (Mathf.Abs(currentValue - targetValue) < valueThreshold && Mathf.Abs(currentVelocity) < velocityThreshold)
    {
        currentValue = targetValue;
        currentVelocity = 0f;
    }
}
2 Likes

Hey thanks Andy, that’s just the kind of thing I was looking for!
P.

Hey @AndyGainey ,

I’ve been using this quite a bit and it’s really great! There is one instance though where I’d like to use it outside of FixedUpdate because the time is scaled to 0. Just wondering what should I multiply by to keep things consistent as Time.fixedDeltaTime wont work in that instance? Say if it was in Update would Time.UnscaledDeltaTime be okay?

Thanks!
Pete